# Introduction to Helm (/docs/deploying-agent-factories-in-the-cloud/81-helm-charts/01-introduction-to-helm)

---
type: Document
title: "Introduction to Helm"
description: "Helm is the package manager for Kubernetes that solves the repetitive YAML problem by templating deployments with parameterized values, enabling consistent multi-environment deployments from a single."
status: stable
order: 8101
chapter: 81
lesson: 1
duration_minutes: 45
generated:
  by: ag-archive/0.1.0
  at: 2026-08-26T00:00:00Z
ksor:
  audience: [public]
  approval:
    by: ag-archive/0.1.0
    at: 2026-08-26T00:00:00Z
trust_tier: unverified
build_id: sha256:c8912c3008ae345a7fdc33ace656531ad74f37c065177057c390c7d759f1e970
source_commit: dad4758bb59919c7b9130444778ade062a2dce3e
ksor_version: 0.0.47
---

When you deploy your Task API to Kubernetes using kubectl, you apply individual YAML files—one for the Deployment, one for the Service, one for the ConfigMap, maybe one for the Secret. That's 5-10 files for a single service.

Now imagine managing this across three environments: dev, staging, production. Each environment needs different resource limits, different image tags, different replicas. You copy and modify the same files ten times, creating drift and bugs.

**Helm solves this problem.** Helm is the package manager for Kubernetes. Instead of managing individual YAML files, you create a **Helm chart**—a templated package that parameterizes your deployment. Change one value file, and your entire Task API deployment adapts automatically.

This lesson teaches you to understand why Helm exists, install it, deploy a public chart, create your own custom chart for your Task API, and manage releases across environments.

---

## Why Helm Exists: The Repetitive YAML Problem

Before Helm, deploying an application to Kubernetes meant writing YAML files by hand:

```yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: task-api
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: task-api
  template:
    metadata:
      labels:
        app: task-api
    spec:
      containers:
      - name: task-api
        image: your-registry/task-api:v1.0.0
        ports:
        - containerPort: 8000
        env:
        - name: DATABASE_URL
          value: "postgresql://..."
        - name: LOG_LEVEL
          value: "INFO"
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"
```

```yaml
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: task-api-service
spec:
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 8000
  selector:
    app: task-api
```

```yaml
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: task-api-config
data:
  log_level: "INFO"
  api_timeout: "30"
```

This works for one environment. But in production, you need:

- **Development**: 1 replica, 256Mi memory, DEBUG logging
- **Staging**: 2 replicas, 512Mi memory, INFO logging
- **Production**: 5 replicas, 1Gi memory, WARN logging

Now you maintain three copies of the same files, edited manually. When you update the base deployment, you must remember to update all three copies. This is error-prone and doesn't scale.

**Helm templating solves this.** Instead of three copies, you write once with **placeholders**:

```yaml
replicas: {{ .Values.replicaCount }}
image: your-registry/task-api:{{ .Values.image.tag }}
```

Then provide three values files—one for each environment:

```yaml
# values-dev.yaml
replicaCount: 1
image:
  tag: v1.0.0-dev
```

```yaml
# values-prod.yaml
replicaCount: 5
image:
  tag: v1.0.0
```

A single `helm install task-api ./task-api-chart -f values-prod.yaml` command deploys the entire stack to production with the correct configuration.

---

## Installing Helm

Helm is a CLI tool you install on your machine. It then communicates with your Kubernetes cluster to deploy charts.

### macOS

```bash
brew install helm
```

**Output:**
```
==> Downloading https://ghcr.io/v2/homebrew/core/helm/manifests/3.14.0
==> Downloading https://ghcr.io/v2/homebrew/core/helm/manifests/3.14.0
==> Downloading https://ghcr.io/v2/homebrew/core/helm/blobs/sha256:abc...
==> Pouring helm--3.14.0.arm64_sonoma.bottle.tar.gz
==> Caveats
Bash completion has been installed to:
  /usr/local/etc/bash_completion.d
Zsh completion has been installed to:
  /opt/homebrew/share/zsh/site-functions
==> Summary
🍺  /opt/homebrew/bin/helm (3.14.0)
```

### Linux

```bash
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
```

**Output:**
```
Downloading https://get.helm.sh/helm-v3.14.0-linux-amd64.tar.gz
Verifying checksum... Done.
Preparing to install helm into /usr/local/bin
helm installed into /usr/local/bin/helm
```

### Windows

```powershell
choco install kubernetes-helm
```

Or using Scoop:
```powershell
scoop install helm
```

### Verify Installation

```bash
helm version
```

**Output:**
```
version.BuildInfo{Version:"v3.14.0", GitCommit:"3fc9f4b2638e76077605842cc9038889c437f325", GitTreeState:"clean", GoVersion:"go1.22.0"}
```

Helm is now installed and ready to deploy charts.

---

## Understanding Helm Charts: The Package Structure

A Helm chart is a directory with a specific structure:

```
task-api-chart/
├── Chart.yaml              # Chart metadata (name, version, description)
├── values.yaml             # Default configuration values
├── values-dev.yaml         # Development-specific overrides
├── values-prod.yaml        # Production-specific overrides
├── templates/              # Kubernetes manifests (Go templates)
│   ├── deployment.yaml     # Pod deployment template
│   ├── service.yaml        # Service template
│   ├── configmap.yaml      # ConfigMap template
│   └── helpers.tpl         # Template helper functions
└── README.md               # Chart documentation
```

Each file serves a purpose:

- **Chart.yaml**: Declares the chart's name, version, description. Think of it like package.json for Kubernetes.
- **values.yaml**: Default values for all placeholders. These are overridable per environment.
- **templates/**: Kubernetes YAML files with Go template syntax (`{{ .Values.replicas }}`).
- **values-dev.yaml, values-prod.yaml**: Environment-specific value overrides.

When you run `helm install`, Helm:
1. Reads Chart.yaml to understand the chart
2. Loads values.yaml (default values)
3. Merges in environment-specific values (e.g., values-prod.yaml)
4. Processes templates/ files, substituting values into placeholders
5. Applies the rendered YAML to Kubernetes

---

## Deploying a Public Chart: Bitnami Redis

Before creating your own chart, let's deploy a public chart to understand how Helm works.

The **Bitnami Helm repository** provides production-ready charts for common applications. Let's add it and install Redis:

### Step 1: Add the Bitnami Repository

```bash
helm repo add bitnami https://charts.bitnami.com/bitnami
```

**Output:**
```
"bitnami" has been added to your repositories
```

### Step 2: Update Repository Index

```bash
helm repo update
```

**Output:**
```
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "bitnami" chart repository
Update Complete. ⎉ Happy Helming!
```

### Step 3: Search for Redis Chart

```bash
helm search repo bitnami/redis
```

**Output:**
```
NAME                 	CHART VERSION	APP VERSION	DESCRIPTION
bitnami/redis        	18.6.0       	7.2.4      	Redis(TM) is an open source, advanced key-value ...
```

### Step 4: Install the Chart

```bash
helm install my-redis bitnami/redis \
  --set auth.enabled=false \
  --set replica.replicaCount=1 \
  --namespace default
```

**Output:**
```
NAME: my-redis
LAST DEPLOYED: Mon Dec 23 10:45:32 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: redis
CHART VERSION: 18.6.0
APP VERSION: 7.2.4
** Please be patient while the chart is being deployed **

Redis&trade; can be accessed via port 6379 on the following DNS name from within your cluster:

    my-redis-master.default.svc.cluster.local

To get your Redis&trade; connection string:

    export REDIS_URL="redis://my-redis-master.default.svc.cluster.local:6379"

To connect to your Redis&trade; instance, run the following commands:

    kubectl run --namespace default redis-cli --rm --tty -i --restart='Never' -- redis-cli -h my-redis-master.default.svc.cluster.local -p 6379
```

### Step 5: Verify the Deployment

```bash
kubectl get pods -n default
```

**Output:**
```
NAME                READY   STATUS    RESTARTS   AGE
my-redis-master-0   1/1     Running   0          45s
```

```bash
helm list
```

**Output:**
```
NAME     	NAMESPACE	REVISION	UPDATED                 	STATUS  	CHART           	APP VERSION
my-redis 	default  	1        	2024-12-23 10:45:32 +0000 UTC	deployed	redis-18.6.0	7.2.4
```

You deployed a production-grade Redis instance with a single `helm install` command. Helm templated all the Redis manifests, applied them to Kubernetes, and created a **release** called `my-redis` that you can upgrade, downgrade, or rollback.

---

## Creating Your Own Helm Chart

Now you'll create a custom chart for your Task API from Chapter 79. Instead of managing raw YAML files, you'll use Helm's templating to create a reusable package.

### Step 1: Generate Chart Scaffold

Helm provides a generator to create the basic chart structure:

```bash
helm create task-api-chart
```

**Output:**
```
Creating task-api-chart
```

```bash
tree task-api-chart
```

**Output:**
```
task-api-chart/
├── Chart.yaml
├── charts
├── templates
│   ├── NOTES.txt
│   ├── _helpers.tpl
│   ├── deployment.yaml
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── service.yaml
│   ├── serviceaccount.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml
```

### Step 2: Examine Chart.yaml

```bash
cat task-api-chart/Chart.yaml
```

**Output:**
```yaml
apiVersion: v2
name: task-api-chart
description: A Helm chart for Kubernetes
type: application
version: 0.1.0
appVersion: "1.16"
```

This declares the chart metadata. Update it for your Task API:

```yaml
apiVersion: v2
name: task-api
description: "Helm chart for deploying Task API on Kubernetes"
type: application
version: 0.1.0
appVersion: "1.0.0"
keywords:
  - task-api
  - fastapi
  - kubernetes
maintainers:
  - name: "Your Name"
    email: "your.email@example.com"
```

### Step 3: Examine values.yaml

```bash
cat task-api-chart/values.yaml
```

**Output:**
```yaml
replicaCount: 1

image:
  repository: nginx
  pullPolicy: IfNotPresent
  tag: ""

imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""

serviceAccount:
  create: true
  annotations: {}
  name: ""

podAnnotations: {}

podSecurityContext: {}

securityContext: {}

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false
  className: ""
  annotations: {}
  hosts:
    - host: chart-example.local
      paths:
        - path: /
          pathType: ImplementationSpecific
  tls: []

resources:
  limits:
    cpu: 100m
    memory: 128Mi
  requests:
    cpu: 100m
    memory: 128Mi

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 100
  targetCPUUtilizationPercentage: 80

nodeSelector: {}

tolerations: []

affinity: {}
```

This is the template of all values. Each value is referenced by name in templates (e.g., `{{ .Values.replicaCount }}`). Update values.yaml for your Task API:

```yaml
replicaCount: 3

image:
  repository: your-registry/task-api
  pullPolicy: Always
  tag: "v1.0.0"

imagePullSecrets:
  - name: registry-secret

service:
  type: ClusterIP
  port: 8000
  targetPort: 8000

resources:
  limits:
    cpu: 500m
    memory: 1Gi
  requests:
    cpu: 250m
    memory: 512Mi

env:
  - name: DATABASE_URL
    value: "postgresql://localhost:5432/tasks"
  - name: LOG_LEVEL
    value: "INFO"
  - name: API_TIMEOUT
    value: "30"
```

### Step 4: Examine the Deployment Template

```bash
cat task-api-chart/templates/deployment.yaml
```

**Output:**
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "task-api-chart.fullname" . }}
  labels:
    {{- include "task-api-chart.labels" . | nindent 4 }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "task-api-chart.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      {{- with .Values.podAnnotations }}
      annotations:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      labels:
        {{- include "task-api-chart.selectorLabels" . | nindent 8 }}
    spec:
      {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      serviceAccountName: {{ include "task-api-chart.serviceAccountName" . }}
      securityContext:
        {{- toYaml .Values.podSecurityContext | nindent 8 }}
      containers:
      - name: {{ .Chart.Name }}
        securityContext:
          {{- toYaml .Values.securityContext | nindent 12 }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        ports:
        - name: http
          containerPort: 8000
          protocol: TCP
        livenessProbe:
          httpGet:
            path: /health
            port: http
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: http
          initialDelaySeconds: 10
          periodSeconds: 5
        resources:
          {{- toYaml .Values.resources | nindent 12 }}
        env:
        {{- if .Values.env }}
        {{- toYaml .Values.env | nindent 10 }}
        {{- end }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
      {{- end }}
```

This template uses Go template syntax:

- `{{ .Values.replicaCount }}` — Substitutes the value of `replicaCount` from values.yaml
- `{{ include "task-api-chart.fullname" . }}` — Calls a helper function (defined in _helpers.tpl)
- `{{- if .Values.autoscaling.enabled }}` — Conditional: include this section only if autoscaling is enabled
- `{{ toYaml . | nindent 8 }}` — Converts a value to YAML and indents it 8 spaces

### Step 5: Test Template Rendering

Before deploying, preview what Helm will generate:

```bash
helm template my-release task-api-chart
```

**Output:**
```yaml
---
# Source: task-api-chart/templates/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-release-task-api-chart
  labels:
    helm.sh/chart: task-api-chart-0.1.0
    app.kubernetes.io/name: task-api-chart
    app.kubernetes.io/instance: my-release
    app.kubernetes.io/version: "1.0.0"
    app.kubernetes.io/managed-by: Helm
---
# Source: task-api-chart/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-release-task-api-chart
  labels:
    helm.sh/chart: task-api-chart-0.1.0
    app.kubernetes.io/name: task-api-chart
    app.kubernetes.io/instance: my-release
    app.kubernetes.io/version: "1.0.0"
    app.kubernetes.io/managed-by: Helm
spec:
  type: ClusterIP
  ports:
    - port: 8000
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: task-api-chart
    app.kubernetes.io/instance: my-release
---
# Source: task-api-chart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-release-task-api-chart
  labels:
    helm.sh/chart: task-api-chart-0.1.0
    app.kubernetes.io/name: task-api-chart
    app.kubernetes.io/instance: my-release
    app.kubernetes.io/version: "1.0.0"
    app.kubernetes.io/managed-by: Helm
spec:
  replicas: 3
  selector:
    matchLabels:
      app.kubernetes.io/name: task-api-chart
      app.kubernetes.io/instance: my-release
  template:
    metadata:
      labels:
        helm.sh/chart: task-api-chart-0.1.0
        app.kubernetes.io/name: task-api-chart
        app.kubernetes.io/instance: my-release
        app.kubernetes.io/version: "1.0.0"
        app.kubernetes.io/managed-by: Helm
    spec:
      serviceAccountName: my-release-task-api-chart
      containers:
      - name: task-api-chart
        image: "your-registry/task-api:v1.0.0"
        imagePullPolicy: Always
        ports:
        - name: http
          containerPort: 8000
          protocol: TCP
        livenessProbe:
          httpGet:
            path: /health
            port: http
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: http
          initialDelaySeconds: 10
          periodSeconds: 5
        resources:
          limits:
            cpu: 500m
            memory: 1Gi
          requests:
            cpu: 250m
            memory: 512Mi
        env:
        - name: MODEL_NAME
          value: "gpt-4"
        - name: LOG_LEVEL
          value: "INFO"
        - name: API_TIMEOUT
          value: "30"
```

Helm rendered the templates with values from values.yaml. The output is valid Kubernetes YAML.

---

## Environment-Specific Values: Dev vs Production

Now create environment-specific value files to customize deployments:

### values-dev.yaml

```yaml
replicaCount: 1

image:
  tag: "v1.0.0-dev"

resources:
  limits:
    cpu: 250m
    memory: 512Mi
  requests:
    cpu: 100m
    memory: 256Mi

env:
  - name: DATABASE_URL
    value: "postgresql://localhost:5432/tasks_dev"
  - name: LOG_LEVEL
    value: "DEBUG"
  - name: API_TIMEOUT
    value: "60"
```

### values-prod.yaml

```yaml
replicaCount: 5

image:
  tag: "v1.0.0"

resources:
  limits:
    cpu: 1000m
    memory: 2Gi
  requests:
    cpu: 500m
    memory: 1Gi

env:
  - name: DATABASE_URL
    value: "postgresql://prod-db:5432/tasks"
  - name: LOG_LEVEL
    value: "WARN"
  - name: API_TIMEOUT
    value: "30"

autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 70
```

### Deploy to Development

```bash
helm install task-api task-api-chart -f task-api-chart/values-dev.yaml
```

**Output:**
```
NAME: task-api
LAST DEPLOYED: Mon Dec 23 11:05:22 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
```

Verify the deployment:

```bash
kubectl get deployment task-api
```

**Output:**
```
NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
task-api   1/1     1            1           15s
```

Notice: 1 replica in dev (from values-dev.yaml).

### Deploy to Production

```bash
helm install task-api-prod task-api-chart -f task-api-chart/values-prod.yaml
```

**Output:**
```
NAME: task-api-prod
LAST DEPLOYED: Mon Dec 23 11:06:45 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
```

Verify:

```bash
kubectl get deployment task-api-prod
```

**Output:**
```
NAME                          READY   UP-TO-DATE   AVAILABLE   AGE
task-api-prod  5/5     5            5           18s
```

Notice: 5 replicas in production (from values-prod.yaml).

---

## Release Management: Upgrade, Rollback, Uninstall

A **release** is an instance of a chart deployed to your cluster. You manage releases with four operations: install, upgrade, rollback, and uninstall.

### Install (Create a Release)

```bash
helm install task-api task-api-chart -f task-api-chart/values-prod.yaml
```

**Output:**
```
NAME: task-api
LAST DEPLOYED: Mon Dec 23 11:15:22 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
```

### Upgrade (Update a Release)

You've pushed a new version of your agent image. Update the release to use it:

```bash
helm upgrade task-api task-api-chart \
  -f task-api-chart/values-prod.yaml \
  --set image.tag="v1.1.0"
```

**Output:**
```
Release "task-api" has been upgraded successfully
NAME: task-api
LAST DEPLOYED: Mon Dec 23 11:16:33 2024
NAMESPACE: default
STATUS: deployed
REVISION: 2
```

Notice the revision changed from 1 to 2. Helm tracks every release change.

Verify the rollout:

```bash
kubectl rollout status deployment/task-api
```

**Output:**
```
deployment "task-api" successfully rolled out
```

### Check Release History

```bash
helm history task-api
```

**Output:**
```
REVISION	UPDATED                 	STATUS    	CHART                  	APP VERSION	DESCRIPTION
1        	Mon Dec 23 11:15:22 2024	superseded	task-api-chart-0.1.0	1.0.0      	Install complete
2        	Mon Dec 23 11:16:33 2024	deployed  	task-api-chart-0.1.0	1.0.0      	Upgrade complete
```

### Rollback (Revert to a Previous Release)

The new version has a bug. Rollback to revision 1:

```bash
helm rollback task-api 1
```

**Output:**
```
Rollback was a success
Release "task-api" has been rolled back to revision 1
```

Verify:

```bash
helm history task-api
```

**Output:**
```
REVISION	UPDATED                 	STATUS    	CHART                  	APP VERSION	DESCRIPTION
1        	Mon Dec 23 11:15:22 2024	superseded	task-api-chart-0.1.0	1.0.0      	Install complete
2        	Mon Dec 23 11:16:33 2024	superseded	task-api-chart-0.1.0	1.0.0      	Upgrade complete
3        	Mon Dec 23 11:17:45 2024	deployed  	task-api-chart-0.1.0	1.0.0      	Rollback to 1
```

A new revision (3) was created that rolls back to revision 1's configuration. The agent is now running the old image again.

### Uninstall (Delete a Release)

```bash
helm uninstall task-api
```

**Output:**
```
release "task-api" uninstalled
```

All Kubernetes resources created by this release are deleted:

```bash
kubectl get deployment
```

**Output:**
```
No resources found in default namespace.
```

---

## What You've Learned

You now understand:

- **Why Helm exists**: It solves the repetitive YAML problem by templating deployments
- **How to install Helm**: On macOS, Linux, and verify installation
- **How to deploy public charts**: Using helm repo add, helm search, and helm install
- **Chart structure**: Chart.yaml, values.yaml, templates/, and helper functions
- **Go template syntax**: Substitution (`{{ .Values.replicaCount }}`), helpers, conditionals
- **Environment-specific values**: Creating separate values files for dev/prod deployments
- **Release management**: Install, upgrade, rollback, and uninstall operations

With these skills, you can package any Kubernetes application—including your Task API—into a reusable, versioned Helm chart that deploys consistently across environments.

---

## Try With AI

Now you'll work with AI to create a multi-environment Helm configuration for your Task API.

**Setup**: You have your Task API container image from Chapter 79 pushed to a registry. You'll parameterize a Helm chart to support three environments (dev, staging, production) with different replicas, resources, and database configurations.

**Prompts**:

1. **Chart Creation with AI**:
   ```
   I have my Task API image at your-registry/task-api:latest from Chapter 79.
   Create a Helm chart called 'task-api' with:
   - Deployment for the Task API (port 8000)
   - Service exposing the API (ClusterIP)
   - ConfigMap for configuration (DATABASE_URL, LOG_LEVEL)
   - Default values: 3 replicas, 500m CPU limit, 1Gi memory limit

   What does the Chart.yaml and values.yaml look like?
   ```

2. **Environment Values with AI**:
   ```
   Now create three values files for the Task API chart:

   values-dev.yaml: 1 replica, 250m CPU, 512Mi memory, DEBUG logging
   values-staging.yaml: 2 replicas, 500m CPU, 1Gi memory, INFO logging
   values-prod.yaml: 5 replicas, 1Gi CPU, 2Gi memory, WARN logging

   Should I use separate files or one values file with conditionals?
   ```

3. **Refinement Based on Response**:
   Ask the AI to clarify:
   - How would you handle the DATABASE_URL secret without storing it in values files?
   - Can I use Helm hooks to run database migrations before the Task API starts?

**Expected**: You'll discover that environment-specific values files are cleaner than conditionals, and learn about Helm's secret management and hooks—features you'll explore in later lessons.

---

## Reflect on Your Skill

You built a `helm-chart` skill in Lesson 0. Test and improve it based on what you learned.

### Test Your Skill

```
Using my helm-chart skill, generate a basic Helm chart for a web service.
Does my skill understand Chart.yaml structure and values.yaml defaults?
```

### Identify Gaps

Ask yourself:
- Did my skill include proper Chart.yaml metadata (apiVersion, name, version, description)?
- Did it generate a sensible values.yaml with image, replicas, and service configuration?
- Does it understand the templates/ directory structure and basic Go template syntax?

### Improve Your Skill

If you found gaps:

```
My helm-chart skill is missing [proper Chart.yaml structure / values organization / template basics].
Update it to include best practices for:
- Chart.yaml metadata fields
- Default values.yaml structure
- Basic Deployment and Service templates
- Environment-specific values files (values-dev.yaml, values-prod.yaml)
```
