Skip to content
StackPractices
intermediate Por Mathias Paulenko

Checklist de Revisión de Helm Charts

Un checklist para revisar Helm charts cubriendo values, templates, security, resource limits, probes, RBAC y best practices.

Temas: testing

Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.

Overview

Un Helm chart review checklist ensure que charts son secure, maintainable y follow Kubernetes best practices. Coverea values schema, template correctness, security contexts, resource limits, health probes, RBAC y chart metadata. Sin un checklist, chart reviews son inconsistent y missean common issues.

When to Use

  • For alternatives, see CI/CD Pipeline Design Template.

  • Reviewéando un new Helm chart antes de publish

  • Auditando existing charts para security o compliance

  • Onboardéando third-party charts a un cluster

  • Upgradeando Helm charts a new Kubernetes versions

  • Estandarizando chart quality across teams

Solution

# Helm Chart Review — `<Chart Name>`

## Review Overview

| Field | Value |
|-------|-------|
| Chart Name | example-app |
| Chart Version | 1.3.0 |
| App Version | 2.1.0 |
| Reviewer | DevOps Team |
| Review Date | 2026-07-05 |
| Kubernetes Version | 1.29+ |
| Helm Version | 3.14+ |

## 1. Chart Structure

| Check | Status | Notes |
|-------|--------|-------|
| Chart.yaml present y valid | ✅ | apiVersion: v2 |
| values.yaml present | ✅ | All defaults documented |
| templates/ directory exists | ✅ | 12 template files |
| _helpers.tpl present | ✅ | Named templates defined |
| .helmignore present | ✅ | Excludes .git, .vscode |
| README.md present | ✅ | Installation y configuration docs |
| values.schema.json present | ✅ | Type validation para all values |
| LICENSE present | ✅ | Apache 2.0 |
| charts/ directory (if dependencies) | ✅ | No subchart dependencies |
| NOTES.txt present | ✅ | Post-install instructions |

### Chart.yaml Validation

| Field | Value | Check |
|-------|-------|-------|
| apiVersion | v2 | ✅ Helm 3 format |
| name | example-app | ✅ Lowercase, no spaces |
| version | 1.3.0 | ✅ Semantic versioning |
| appVersion | 2.1.0 | ✅ Application version |
| description | Example web application | ✅ Descriptive |
| type | application | ✅ Application chart |
| keywords | [web, app, frontend] | ✅ Searchable |
| maintainers | [{name: devops-team, email: devops@example.com}] | ✅ Contact info |
| icon | https://example.com/icon.png | ✅ Chart icon |

## 2. Values Schema

### values.yaml Review

| Check | Status | Notes |
|-------|--------|-------|
| No secrets en values.yaml | ✅ | Secrets via --set o external secret store |
| All values tienen comments | ✅ | Every key documented |
| Sensible defaults | ✅ | Chart funciona con no overrides |
| No hardcoded environment values | ✅ | Environment-specific values overridden |
| Image tag usa appVersion | ✅ | `{{ .Chart.AppVersion }}` |
| Replica count defaults a > 1 | ✅ | Default: 3 |
| Resources tienen defaults | ✅ | requests y limits set |
| Service type es configurable | ✅ | Default: ClusterIP |

### values.schema.json Review

| Check | Status | Notes |
|-------|--------|-------|
| All values tienen type definitions | ✅ | String, integer, boolean, object |
| Required fields marked | ✅ | image.repository required |
| Enum values para restricted fields | ✅ | serviceType: [ClusterIP, NodePort, LoadBalancer] |
| Minimum/maximum para numeric fields | ✅ | replicas: min 1, max 100 |
| Pattern para string fields | ✅ | image.repository: non-empty string |
| Additional properties disabled | ✅ | additionalProperties: false |

### Values Structure

```yaml
# values.yaml
replicaCount: 3

image:
  repository: ghcr.io/example/app
  pullPolicy: IfNotPresent
  tag: ""  # Overrides .Chart.AppVersion

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

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

podAnnotations: {}
podLabels: {}

podSecurityContext:
  runAsNonRoot: true
  runAsUser: 10001
  runAsGroup: 10001
  fsGroup: 10001
  seccompProfile:
    type: RuntimeDefault

securityContext:
  allowPrivilegeEscalation: false
  readOnlyRootFilesystem: true
  capabilities:
    drop:
      - ALL

service:
  type: ClusterIP
  port: 8080

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

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

livenessProbe:
  httpGet:
    path: /health
    port: http
  initialDelaySeconds: 30
  periodSeconds: 10
  timeoutSeconds: 5
  failureThreshold: 3

readinessProbe:
  httpGet:
    path: /ready
    port: http
  initialDelaySeconds: 5
  periodSeconds: 5
  timeoutSeconds: 3
  failureThreshold: 3

autoscaling:
  enabled: false
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 80
  targetMemoryUtilizationPercentage: 80

volumes: []
volumeMounts: []

nodeSelector: {}
tolerations: []
affinity: {}

podDisruptionBudget:
  enabled: false
  minAvailable: 1

3. Template Review

Template Best Practices

CheckStatusNotes
No hardcoded namespacesUsa .Release.Namespace
Labels follow conventionsapp.kubernetes.io/* labels
Selectors matchean labelsConsistent across deployments y services
Named templates para repeated blocks_helpers.tpl para labels, names
No trailing whitespace{{- }} y {{- -}} used
Include comments para complex logicConditional blocks documented
No template errors en empty valuesDefaults via
Helm hooks used correctlypost-install, pre-delete hooks

Labels Checklist

LabelPresentValue
app.kubernetes.io/name{{ include “chart.name” . }}
app.kubernetes.io/instance{{ .Release.Name }}
app.kubernetes.io/version{{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by{{ .Release.Service }}
app.kubernetes.io/componentfrontend
app.kubernetes.io/part-ofexample-app

Template Files

FileCheckStatus
deployment.yamlCorrect API version (apps/v1)
deployment.yamlStrategy configurable
deployment.yamlImage pull policy configurable
service.yamlCorrect API version (v1)
service.yamlPort configurable
service.yamlSelector matchea deployment labels
ingress.yamlCorrect API version (networking.k8s.io/v1)
ingress.yamlPath type specified
ingress.yamlTLS configurable
serviceaccount.yamlCorrect API version (v1)
serviceaccount.yamlConditional creation
configmap.yamlCorrect API version (v1)
configmap.yamlData desde values
hpa.yamlCorrect API version (autoscaling/v2)
hpa.yamlConditional en autoscaling.enabled
pdb.yamlCorrect API version (policy/v1)
pdb.yamlConditional en podDisruptionBudget.enabled
NOTES.txtPost-install instructions

4. Security Review

Pod Security Context

CheckStatusNotes
runAsNonRoot: truePod corre como non-root
runAsUser: non-zero UIDUID 10001
runAsGroup: non-zero GIDGID 10001
fsGroup: non-zeroGID 10001
seccompProfile: RuntimeDefaultSeccomp enabled

Container Security Context

CheckStatusNotes
allowPrivilegeEscalation: falseNo privilege escalation
readOnlyRootFilesystem: trueRead-only filesystem
capabilities.drop: [ALL]All Linux capabilities dropped
runAsNonRoot: true (container-level)Redundant con pod-level
privileged: falseNot running privileged

RBAC

CheckStatusNotes
Service account conditionalserviceAccount.create flag
RBAC least privilegeOnly required resources
No cluster-admin roleNo cluster-wide permissions
Role binding a service accountNamespace-scoped
Automount token configurableserviceAccount.automount flag

Image Security

CheckStatusNotes
Image digest pinned⚠️Usa tag, no digest
No :latest tagUsa appVersion
Pull policy: IfNotPresentDefault pull policy
Private registry supportimagePullSecrets configurable
Image scanned en CITrivy scan en pipeline

5. Resource Management

Resource Limits

CheckStatusNotes
CPU request set100m
CPU limit set500m
Memory request set128Mi
Memory limit set512Mi
Memory request < limit128Mi < 512Mi
CPU request < limit100m < 500m
Resources configurable via valuesresources en values.yaml
QoS class: BurstableRequests < limits

Horizontal Pod Autoscaler

CheckStatusNotes
HPA conditionalautoscaling.enabled flag
Min replicas >= 2minReplicas: 3
Max replicas reasonablemaxReplicas: 10
CPU target < 90%80%
Memory target < 90%80%
HPA API version: autoscaling/v2Current API

Pod Disruption Budget

CheckStatusNotes
PDB conditionalpodDisruptionBudget.enabled flag
minAvailable o maxUnavailable setminAvailable: 1
PDB API version: policy/v1Current API

6. Health Probes

Liveness Probe

CheckStatusNotes
Liveness probe definedHTTP GET /health
initialDelaySeconds set30 seconds
periodSeconds reasonable10 seconds
timeoutSeconds set5 seconds
failureThreshold reasonable3 failures
Probe configurable via valueslivenessProbe en values

Readiness Probe

CheckStatusNotes
Readiness probe definedHTTP GET /ready
initialDelaySeconds set5 seconds
periodSeconds reasonable5 seconds
timeoutSeconds set3 seconds
failureThreshold reasonable3 failures
Probe configurable via valuesreadinessProbe en values

Startup Probe (if applicable)

CheckStatusNotes
Startup probe definedPara slow-starting apps
failureThreshold high enough30 failures
periodSeconds: 105 min startup window

7. Deployment Strategy

CheckStatusNotes
Strategy configurableRollingUpdate default
Max unavailable set25%
Max surge set25%
Revision history limit10
Progress deadline seconds600

8. Testing

Helm Tests

CheckStatusNotes
tests/ directory presentHelm test templates
Test connects a servicecurl a service endpoint
Test verifies healthHTTP 200 desde /health
Test corre via helm testTest pod usa helm.sh/hook: test

Linting

CheckStatusNotes
helm lint passesNo warnings
kubeval passesSchema validation
kube-score passesBest practices check
yamllint passesYAML formatting

## Explanation

Un Helm chart review checklist ensure que charts son production-ready. El review coverea eight areas: structure, values, templates, security, resources, probes, deployment strategy y testing. Cada area tiene specific checks que catchean common issues antes de que lleguen a production.

Chart structure verify que all required files existen y follow conventions. Chart.yaml debe usar apiVersion v2 (Helm 3), values.yaml debe tener sensible defaults y values.schema.json debe validate types. Missing files o invalid structure causan Helm commands a fail.

Values review checkea que defaults son safe y no secrets están hardcoded. Secrets en values.yaml son un common security issue — terminan en version control. El chart debería usar external secret stores o --set para secrets. All values deberían tener comments explainando su purpose.

Template review checkea Kubernetes API versions, label consistency y conditional rendering. Labels deben follow el app.kubernetes.io/* convention para discoverability. Selectors deben matchear labels across deployments y services. Trailing whitespace en templates causa rendering issues.

Security review es el most critical section. Pods deben correr como non-root con read-only filesystems y no privilege escalation. All Linux capabilities se deberían drop. RBAC debería follow least privilege — no cluster-admin roles. Images no deberían usar :latest tags y deberían scanneearse para vulnerabilities.

Resource management ensure que pods tienen CPU y memory requests y limits. Sin limits, un single pod puede consume all cluster resources. El HPA debería ser configurable con sensible defaults. El PDB ensure availability durante voluntary disruptions.

Health probes le dicen a Kubernetes cuándo un pod está alive y ready. Liveness probes detectan deadlocks y triggerean restarts. Readiness probes controlan traffic routing. Sin probes, Kubernetes no puede distinguish un healthy pod de un broken one.

## Variants

| Context | Approach | Notes |
|---------|----------|-------|
| Library chart | Focus en named templates | No resources rendered |
| Operator chart | Addeá CRD review | Custom resource definitions |
| StatefulSet chart | Addeá PVC y ordering checks | VolumeClaimTemplates, podManagementPolicy |
| Job/CronJob chart | Addeá restartPolicy y concurrency checks | BackoffLimit, concurrencyRule |
| Multi-tenant | Addeá namespace isolation checks | NetworkPolicies, PSPs |
| Air-gapped | Verify no external dependencies | All images en private registry |

## What Works

1. Usá values.schema.json — catchea type errors antes de deployment
2. Pineá image digests, no solo tags — tags pueden mutarse
3. Dropé all capabilities, add back solo las needed — least privilege
4. Seteá resource requests y limits — prevení resource starvation
5. Definí both liveness y readiness probes — Kubernetes necesita both
6. Usá helm lint y kube-score en CI — automatizá el checklist
7. Documentá every value en values.yaml — users necesitan saber qué hace cada uno

## Common Mistakes

1. Secrets en values.yaml — committed a version control
2. No resource limits — pods consumen all cluster resources
3. No probes — Kubernetes no puede detectar unhealthy pods
4. Running como root — unnecessary privilege
5. No PDB — voluntary disruptions causan downtime
6. Usando :latest tag — unpredictable deployments
7. No values.schema.json — type errors no se catchean hasta deployment

## Frequently Asked Questions

### ¿Deberíamos pinear image digests en vez de tags?

Sí, para production charts. Tags son mutable — el same tag puede pointear a different images over time. Digests son immutable — el same digest siempre pointea a la same image. Pinear digests ensure reproducible deployments. El trade-off es que updatear el image require updatear el chart, pero esto es desirable para production.

### ¿Cómo handleamos secrets en Helm charts?

Nunca pongas secrets en values.yaml. Usá uno de estos approaches: (1) --set para individual secrets at deploy time, (2) External secret stores como HashiCorp Vault o AWS Secrets Manager con External Secrets Operator, (3) Sealed Secrets para encrypt secrets en Git. Los tres keep secrets out de version control.

### ¿Cuál es la difference entre helm lint y kube-score?

helm lint checkea chart structure y template syntax — catchea Helm-specific issues como invalid Chart.yaml o template errors. kube-score checkea los rendered Kubernetes manifests against best practices — catchea Kubernetes-specific issues como missing probes o resource limits. Usá both: helm lint para chart quality, kube-score para manifest quality.

### ¿Deberíamos usar Helm hooks para database migrations?

Depende. Helm hooks como post-install y post-upgrade pueden correr migrations automáticamente. Esto es convenient pero risky: si el migration faila, el release está en un inconsistent state. Para critical databases, corré migrations fuera de Helm como un separate step en el CI/CD pipeline. Para simple migrations, Helm hooks son acceptable.

### ¿Cómo testeamos Helm charts?

Three levels: (1) helm lint para syntax y structure, (2) helm template + kubeval para manifest validation, (3) helm test para runtime tests contra un deployed chart. Para CI, corré helm lint y helm template en every PR. Para staging, deployeá el chart y corré helm test. Para production, usá helm upgrade --dry-run para preview changes.