Helm Values JSON Schema: Valida tu values.yaml Antes de Romper Producción

Helm Values JSON Schema: Valida tu values.yaml Antes de Romper Producción

Helm es el gestor de paquetes de facto para Kubernetes, y values.yaml es su interfaz principal de configuración. Durante años, sin embargo, esa interfaz ha estado completamente sin validar por defecto — un fichero YAML de forma libre donde cualquier clave puede ser cualquier cosa, donde los errores tipográficos pasan silenciosamente, y donde las implantaciones mal configuradas solo se revelan cuando los pods fallan al arrancar en producción. El fichero values.schema.json cambia esa ecuación por completo. Este artículo explica por qué importa la validación de schemas, cómo implementarla correctamente y cómo integrarla en un pipeline de CI/CD moderno.

El Problema: Fallos Silenciosos en Producción

Imaginad un equipo de plataforma gestionando docenas de releases de Helm en múltiples clústeres. Un desarrollador envía un fichero de valores con replicaCount: "3" en lugar de replicaCount: 3 — una cadena de texto donde se espera un entero. O escribe image.pullPolicy: Allways con una errata. O no incluye una referencia a un secret obligatorio que la aplicación necesita para arrancar. En los tres casos, Helm sin validación de schema renderizará felizmente los templates, producirá manifiestos de Kubernetes y los aplicará al clúster. El fallo aparece después — a veces mucho después — como un CrashLoopBackOff, un ImagePullBackOff o un error sutil en tiempo de ejecución que lleva horas depurar.

No es un escenario hipotético. Es la realidad diaria de los equipos que operan a escala sin validación de valores. La causa raíz es arquitectónica: los templates de Helm usan el motor text/template de Go, que es débilmente tipado y permisivo por diseño. Un template que hace {{ .Values.replicaCount }} renderizará tanto si el valor es un entero como si es una cadena o incluso un booleano. El manifiesto de Kubernetes resultante puede ser inválido, pero ese error solo sale a la superficie cuando el API server de Kubernetes lo rechaza — o peor, lo acepta pero lo interpreta de manera diferente a la prevista.

Las consecuencias se amplifican a escala. Cuando un chart lo utilizan varios equipos, la ausencia de un contrato formal para los valores aceptables obliga a cada consumidor a leer los ficheros de template y los comentarios para entender qué entradas son válidas. No hay especificación legible por máquina. No hay soporte en el IDE. No hay ningún guardarraíl. La única documentación es lo que el autor del chart tuvo a bien escribir en los comentarios de values.yaml — y los comentarios no detienen a un pipeline de CI de desplegar una implantación rota.

Qué es values.schema.json

Desde Helm 3.0.0, publicado en noviembre de 2019, Helm soporta un fichero opcional values.schema.json en la raíz del directorio del chart — al mismo nivel que Chart.yaml y values.yaml. Este fichero es un documento JSON Schema draft-07 que describe formalmente la estructura, los tipos, las restricciones y los campos obligatorios de los valores del chart.

Cuando este fichero está presente, Helm valida automáticamente los valores combinados (los valores por defecto de values.yaml fusionados con cualquier sobreescritura del usuario) contra el schema en varios puntos: durante helm install, helm upgrade, helm template y helm lint. Si la validación falla, Helm se niega a continuar e imprime un mensaje de error legible que identifica exactamente qué valor ha fallado y por qué. Esto transforma una clase de fallos en tiempo de ejecución en fallos en tiempo de construcción — la dirección correcta para cualquier sistema en producción.

Vale la pena destacar la elección específica de JSON Schema draft-07. El draft-07 tiene un soporte muy amplio en herramientas, incluyendo la extensión YAML de Red Hat para VS Code, los IDEs de JetBrains y la mayoría de los validadores de JSON Schema. Introdujo las palabras clave condicionales if/then/else que son especialmente útiles para los Helm charts. Las versiones más recientes (2019-09, 2020-12) ofrecen funcionalidades adicionales, pero tienen menos soporte universal en herramientas, lo que hace que draft-07 sea la elección pragmática para los autores de charts hoy en día.

Estructura del Directorio del Chart

my-app/
├── Chart.yaml
├── values.yaml
├── values.schema.json      ← va aquí
├── charts/
└── templates/
    ├── deployment.yaml
    ├── service.yaml
    ├── ingress.yaml
    └── _helpers.tpl

El fichero de schema se incluye cuando un chart se empaqueta con helm package y se distribuye a través de repositorios de charts. Los consumidores del chart obtienen la validación del schema automáticamente sin ninguna configuración adicional — los guardarraíles se distribuyen con el propio chart.

Cómo Utiliza Helm el Schema

El comportamiento de validación de Helm es sencillo, pero tiene algunos matices que merece la pena entender. Cuando Helm procesa un release, primero combina todas las fuentes de valores en orden de precedencia creciente: los valores por defecto del chart (values.yaml), los valores del chart padre, los ficheros de valores pasados con -f y, por último, los flags --set. El resultado combinado se valida entonces contra el schema en una única operación.

Esto significa que el schema valida los valores efectivos, no cada fuente de forma aislada. Un campo obligatorio que tiene un valor por defecto en values.yaml pasará la validación aunque el usuario no lo especifique, porque el resultado combinado incluye el valor por defecto. Este es el comportamiento correcto: valida lo que se usará realmente durante el renderizado.

La validación ocurre antes del renderizado de los templates. Si la validación del schema falla, Helm termina con un código de estado no cero e imprime todos los errores de validación. La salida de error es estructurada y accionable:

$ helm install my-release ./my-app --set replicaCount=abc

Error: values don't meet the specifications of the schema(s) in the following chart(s):
my-app:
- replicaCount: Invalid type. Expected: integer, given: string

Para helm lint, que se usa típicamente en pipelines de CI sin instalar en un clúster, la validación del schema también se ejecuta. Esto convierte a helm lint en una potente puerta de pre-despliegue cuando hay ficheros de schema presentes.

Ventajas en el IDE: Autocompletado y Validación Inline

Más allá de la propia validación de Helm, values.schema.json desbloquea el soporte en el IDE que mejora significativamente la experiencia del desarrollador al trabajar con ficheros de valores. La extensión YAML de Red Hat para VS Code puede referenciar un fichero JSON Schema para proporcionar autocompletado, comprobación de tipos y resaltado de errores inline en ficheros YAML.

Para habilitarlo, añadid una configuración yaml.schemas a los ajustes del workspace de VS Code o al fichero de ajustes del usuario:

// .vscode/settings.json
{
  "yaml.schemas": {
    "./my-app/values.schema.json": "./my-app/values.yaml"
  }
}

Con esta configuración, al editar values.yaml en VS Code se mostrará autocompletado para las claves definidas, errores inline para los errores de tipo y documentación en hover extraída de los campos description de vuestro schema. Para los equipos de plataforma que mantienen Helm charts internos, esto transforma el chart en una interfaz de configuración autodocumentada y preparada para el IDE — sin ninguna inversión adicional en herramientas.

Los IDEs de JetBrains (IntelliJ IDEA, GoLand, etc.) soportan las asociaciones de JSON Schema a través del panel de ajustes Languages & Frameworks > Schemas and DTDs > JSON Schema Mappings, proporcionando una funcionalidad equivalente para los equipos que usan esas herramientas.

Construyendo el Schema: Guía Práctica

Construyamos un ejemplo completo y realista. Partimos de un values.yaml típico para un chart de aplicación web:

# values.yaml
replicaCount: 2

image:
  repository: myorg/my-app
  tag: "1.0.0"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: false
  hostname: ""
  tls: false

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

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

config:
  logLevel: info
  databaseUrl: ""

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

Ahora el values.schema.json completo que valida esta estructura:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "my-app Helm Chart Values",
  "description": "Valores de configuración para el Helm chart my-app",
  "type": "object",
  "additionalProperties": false,
  "required": ["image", "service"],
  "$defs": {
    "resourceQuantity": {
      "type": "string",
      "pattern": "^[0-9]+(\\.[0-9]+)?(m|Ki|Mi|Gi|Ti|Pi|Ei|k|M|G|T|P|E)?$",
      "description": "Una cantidad de recurso de Kubernetes (p. ej.: 100m, 128Mi, 1Gi)"
    }
  },
  "properties": {
    "replicaCount": {
      "type": "integer",
      "minimum": 0,
      "maximum": 50,
      "default": 2,
      "description": "Número de réplicas del pod. Estableced a 0 para escalar a cero."
    },
    "image": {
      "type": "object",
      "additionalProperties": false,
      "required": ["repository", "tag"],
      "description": "Configuración de la imagen del contenedor",
      "properties": {
        "repository": {
          "type": "string",
          "minLength": 1,
          "description": "Repositorio de la imagen del contenedor"
        },
        "tag": {
          "type": "string",
          "pattern": "^[a-zA-Z0-9._-]+$",
          "minLength": 1,
          "description": "Tag de la imagen. Evitad usar 'latest' en producción."
        },
        "pullPolicy": {
          "type": "string",
          "enum": ["Always", "IfNotPresent", "Never"],
          "default": "IfNotPresent",
          "description": "imagePullPolicy de Kubernetes"
        }
      }
    },
    "service": {
      "type": "object",
      "additionalProperties": false,
      "required": ["type", "port"],
      "properties": {
        "type": {
          "type": "string",
          "enum": ["ClusterIP", "NodePort", "LoadBalancer", "ExternalName"],
          "description": "Tipo de Service de Kubernetes"
        },
        "port": {
          "type": "integer",
          "minimum": 1,
          "maximum": 65535,
          "description": "Puerto del Service"
        }
      }
    },
    "ingress": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "enabled": {
          "type": "boolean",
          "default": false
        },
        "hostname": {
          "type": "string"
        },
        "tls": {
          "type": "boolean",
          "default": false
        }
      },
      "if": {
        "properties": {
          "enabled": { "const": true }
        },
        "required": ["enabled"]
      },
      "then": {
        "required": ["hostname"],
        "properties": {
          "hostname": {
            "type": "string",
            "minLength": 1,
            "pattern": "^[a-zA-Z0-9][a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
            "description": "Nombre de host para el Ingress. Obligatorio cuando ingress está habilitado."
          }
        }
      }
    },
    "resources": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "requests": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "cpu": { "$ref": "#/$defs/resourceQuantity" },
            "memory": { "$ref": "#/$defs/resourceQuantity" }
          }
        },
        "limits": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "cpu": { "$ref": "#/$defs/resourceQuantity" },
            "memory": { "$ref": "#/$defs/resourceQuantity" }
          }
        }
      }
    },
    "autoscaling": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "enabled": { "type": "boolean", "default": false },
        "minReplicas": { "type": "integer", "minimum": 1 },
        "maxReplicas": { "type": "integer", "minimum": 1 },
        "targetCPUUtilizationPercentage": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100
        }
      }
    },
    "config": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "logLevel": {
          "type": "string",
          "enum": ["debug", "info", "warn", "error"],
          "default": "info"
        },
        "databaseUrl": {
          "type": ["string", "null"],
          "description": "URL de conexión a la base de datos."
        }
      }
    },
    "nodeSelector": {
      "type": "object",
      "description": "Selectores de nodo de Kubernetes"
    },
    "tolerations": {
      "type": "array",
      "description": "Tolerations de Kubernetes"
    },
    "affinity": {
      "type": "object",
      "description": "Reglas de afinidad de Kubernetes"
    }
  }
}

Patrones Clave del Schema Explicados

additionalProperties: false

Este es, posiblemente, el patrón más importante de un Helm schema. Sin él, las claves desconocidas pasan la validación silenciosamente — lo que anula gran parte del propósito. Con "additionalProperties": false, cualquier clave no listada en properties provoca un error de validación. Esto detecta erratas como repicaCount en lugar de replicaCount, que de otro modo usarían silenciosamente el valor por defecto y dejarían al desarrollador preguntándose por qué su sobreescritura no tuvo efecto.

Aplicadlo en cada nivel de objeto anidado, no solo en la raíz. Una errata dentro de image: o resources: es tan peligrosa como una en el nivel superior.

$defs para Definiciones Reutilizables

La palabra clave $defs (llamada definitions en versiones anteriores del draft, aunque draft-07 soporta ambas) proporciona un espacio de nombres para fragmentos de schema reutilizables. En el ejemplo anterior, resourceQuantity se define una vez y se referencia mediante $ref tanto en requests como en limits. Esto evita la duplicación y garantiza una lógica de validación consistente en campos relacionados.

Para charts más grandes, $defs se vuelve imprescindible. Los patrones habituales incluyen schemas reutilizables para configuraciones de imagen, requisitos de recursos, configuraciones de probe y mapas de variables de entorno.

Validación Condicional con if/then/else

La construcción if/then/else en JSON Schema draft-07 es especialmente potente para los Helm charts, donde muchos valores son condicionales según un flag de activación de funcionalidad. El ejemplo del ingress lo demuestra: cuando ingress.enabled es true, el campo hostname se vuelve obligatorio y debe coincidir con un patrón de nombre de host válido. Cuando el ingress está deshabilitado, el hostname puede estar vacío u omitirse por completo.

Este patrón puede extenderse para escenarios más complejos. Por ejemplo, para que cuando autoscaling.enabled sea true, el replicaCount independiente no deba establecerse (ya que el HPA controla el número de réplicas):

{
  "if": {
    "properties": {
      "autoscaling": {
        "properties": {
          "enabled": { "const": true }
        },
        "required": ["enabled"]
      }
    }
  },
  "then": {
    "properties": {
      "replicaCount": {
        "description": "replicaCount se ignora cuando autoscaling está habilitado"
      }
    }
  }
}

Validación de Patrones para Tags de Imagen

El campo del tag de imagen es una fuente habitual de problemas en producción. Los equipos despliegan accidentalmente con latest, que es no determinista y hace que los rollbacks sean poco fiables. Una restricción de patrón puede forzar la versión semántica o, como mínimo, prohibir el tag latest en los charts de producción:

"tag": {
  "type": "string",
  "not": {
    "enum": ["latest", ""]
  },
  "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+",
  "description": "Se requiere un tag de versión semántica. 'latest' no está permitido."
}

Esto fuerza que los tags de imagen comiencen con un número de versión semántica, rechazando inmediatamente latest, cadenas vacías o nombres de rama arbitrarios que producirían implantaciones no reproducibles.

Enum para Vocabularios Controlados

Los campos con un conjunto fijo de valores válidos — tipos de Service de Kubernetes, políticas de pull de imágenes, niveles de log — deben usar enum. Esto es más preciso que un patrón y produce mensajes de error más claros. También permite que el autocompletado del IDE muestre exactamente las opciones válidas como una lista de selección, en lugar de requerir que el desarrollador recuerde o busque los valores aceptables.

Integración en CI/CD

GitHub Actions

El punto de integración más directo es helm lint, que ejecuta la validación del schema como parte de sus comprobaciones. Un workflow mínimo de GitHub Actions que valida un chart en cada pull request es el siguiente:

# .github/workflows/helm-lint.yaml
name: Helm Lint

on:
  pull_request:
    paths:
      - 'charts/**'

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Helm
        uses: azure/setup-helm@v4
        with:
          version: '3.14.0'

      - name: Lint chart con valores por defecto
        run: helm lint charts/my-app

      - name: Lint chart con valores de staging
        run: helm lint charts/my-app -f charts/my-app/ci/staging-values.yaml

      - name: Lint chart con valores de producción
        run: helm lint charts/my-app -f charts/my-app/ci/production-values.yaml

      - name: Validar renderizado de templates
        run: |
          helm template my-app charts/my-app \
            -f charts/my-app/ci/production-values.yaml \
            --debug > /dev/null

La convención del directorio ci/ (ficheros de valores específicos para pruebas en CI) es un patrón de la herramienta chart-testing y funciona bien para validar múltiples combinaciones de valores realistas, no solo los valores por defecto.

Para los equipos que usan la herramienta CLI ct (chart-testing) del proyecto Helm, la validación del schema se incluye automáticamente en el comando ct lint, que también gestiona las comprobaciones de versiones del chart y el linting de YAML:

      - name: Chart Testing lint
        uses: helm/chart-testing-action@v2.6.1

      - name: Ejecutar chart-testing lint
        run: ct lint --target-branch ${{ github.event.repository.default_branch }}

Hooks de Pre-commit

Para el desarrollo local, los hooks de pre-commit detectan los problemas antes de que el código se suba al repositorio. El framework pre-commit hace esto sencillo:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gruntwork-io/pre-commit
    rev: v0.1.23
    hooks:
      - id: helmlint

  - repo: local
    hooks:
      - id: helm-schema-validate
        name: Helm Schema Validation
        language: script
        entry: scripts/validate-helm-schemas.sh
        files: ^charts/.*values.*\.yaml$
#!/usr/bin/env bash
# scripts/validate-helm-schemas.sh
set -euo pipefail

for chart_dir in charts/*/; do
  if [[ -f "${chart_dir}/values.schema.json" ]]; then
    echo "Linting ${chart_dir}..."
    helm lint "${chart_dir}" --strict
  fi
done

Integración con ArgoCD y Flux

Tanto ArgoCD como Flux (Helm Controller) invocan helm template internamente al reconciliar los Helm releases. Dado que helm template ejecuta la validación del schema cuando hay un fichero de schema presente, cualquier valor inválido en un manifiesto HelmRelease de Flux o en una Application de ArgoCD hará que la reconciliación falle con un mensaje de error claro — visible en los logs del controlador y que aparece como un estado degradado del recurso. No se requiere configuración adicional; la validación del schema es automática.

Generando Schemas a Partir de Charts Existentes

Para charts que ya tienen un values.yaml bien estructurado, escribir un schema desde cero es laborioso pero no se parte de cero. Varias herramientas pueden generar un schema borrador que luego se refina:

  • helm-values-schema-json — un plugin de Helm (helm plugin install https://github.com/losisin/helm-values-schema-json) que inspecciona values.yaml y genera un schema borrador con tipos inferidos. Ejecutadlo con helm schema-gen values.yaml.
  • Herramientas online de generación de JSON Schema — pegad vuestros valores como JSON (convertid primero el YAML a JSON) y obtendréis un schema borrador.
  • Manualmente desde cero — para charts nuevos, escribir el schema junto con el fichero de valores desde el principio es el enfoque más preciso y no requiere herramientas adicionales.

Los schemas generados siempre son puntos de partida. Infieren tipos a partir de los valores existentes, pero no pueden conocer las restricciones previstas, los enums, los patrones, los campos obligatorios en casos condicionales ni additionalProperties: false en los niveles anidados. La revisión y refinamiento manual es siempre necesaria.

Errores Comunes y Cómo Evitarlos

ErrorSíntomaSolución
Sin additionalProperties: falseLas erratas en nombres de claves pasan la validación silenciosamenteAñadidlo en cada nivel de objeto, incluyendo los anidados
Schema solo en la raízLas erratas en niveles anidados no se detectanAplicad additionalProperties: false recursivamente
Sin valores por defecto en el schemaEl IDE muestra campos como obligatorios cuando son opcionalesAñadid default a todos los campos opcionales
Patrones demasiado estrictos que bloquean valores válidosImplantaciones legítimas fallan la validación del schemaProbad los patrones contra vuestro espacio de valores real antes de publicar
Usar definitions en lugar de $defsFunciona en la mayoría de herramientas pero es terminología de draft-2019-09+Usad $defs para cumplimiento con draft-07; ambos funcionan en la práctica
Schema no comprometido en el repositorio del chartLos consumidores no obtienen validación al obtener el chart del repositorioIncluir siempre values.schema.json junto al chart
Validar valores de subcharts desde el schema del chart padreErrores de schema para valores de subcharts que el padre no poseeNo intentéis validar valores de subcharts en el schema del padre; cada chart es dueño de su propio schema

El Problema de los Valores Nulos

Un problema sutil pero común: en YAML, una clave no establecida sin valor (clave:) se resuelve como null, no como una cadena vacía o un cero. Si vuestro schema define un campo como "type": "string", un valor nulo fallará la validación. Para gestionar campos opcionales que los usuarios podrían dejar en blanco, utilizad una unión de tipos:

"databaseUrl": {
  "type": ["string", "null"],
  "description": "URL de conexión a la base de datos. Dejad null para usar el valor por defecto."
}

Como alternativa, aseguraos de que los valores por defecto en values.yaml usen cadenas vacías ("") en lugar de claves sin valor, y documentad esa convención para los consumidores del chart.

Desviación del Schema

A medida que los charts evolucionan, se añaden nuevos valores a values.yaml sin las correspondientes actualizaciones en values.schema.json. Con el tiempo, el schema queda obsoleto y proporciona una cobertura parcial. La solución es procedimental: tratad las actualizaciones del schema como parte de la definición de hecho para cualquier PR que modifique los valores. La revisión de código debe incluir la comprobación de que los valores nuevos o modificados tienen entradas correspondientes en el schema.

Un patrón útil es incluir la validación del schema como paso obligatorio en la checklist de revisión del chart:

  • ¿Se ha añadido el nuevo campo a values.yaml con un valor por defecto sensato?
  • ¿Se ha añadido la entrada correspondiente en values.schema.json con el tipo correcto?
  • ¿Se ha ejecutado helm lint localmente con los valores de CI para verificar que pasa la validación?

Preguntas Frecuentes

¿Valida values.schema.json los valores de los subcharts?

No. Cada chart en una relación de dependencia valida únicamente sus propios valores contra su propio schema. Si el chart A depende del chart B, y el chart B tiene un schema, el schema del chart B valida los valores bajo la clave b: en el values.yaml del chart A — pero solo cuando se procesa en el contexto del chart B. El schema del chart A no debe intentar describir la estructura de valores del chart B. Esto es por diseño: mantiene el acoplamiento flexible entre charts y permite que los subcharts evolucionen sus schemas de forma independiente.

¿Puedo usar JSON Schema draft-2020-12 en lugar de draft-07?

Técnicamente, Helm no fuerza estrictamente qué versión del draft usáis — utiliza la librería Go github.com/xeipuuv/gojsonschema, que soporta draft-04 hasta draft-07. El uso de palabras clave de drafts más nuevos que no están soportadas por esta librería puede provocar que se ignoren silenciosamente en lugar de lanzar un error. Para el soporte en el IDE, draft-07 tiene la compatibilidad más amplia. Si necesitáis características de drafts más nuevos (como unevaluatedProperties de 2020-12), probad cuidadosamente para confirmar que las hace cumplir el validador de Helm y no se omiten silenciosamente.

¿Cómo gestiono valores que difieren entre entornos sin conflictos de schema?

El schema debe describir todos los valores válidos en todos los entornos. Usad enum para enumerar todos los valores válidos para un campo, y usad if/then/else para restricciones que solo aplican en ciertas configuraciones. El schema es un contrato de lo que acepta el chart, no una política de lo que debe usar un entorno específico. Las políticas específicas de entorno (como «producción debe usar un mínimo de 3 réplicas») se aplican mejor en un nivel superior — mediante controladores de admisión como OPA Gatekeeper o Kyverno — en lugar de en el schema del chart.

¿Se ejecuta la validación del schema cuando se usa helm template para dry runs?

Sí. helm template ejecuta la validación del schema antes de renderizar los templates. Esto lo hace útil como paso de validación en pipelines de CI incluso sin un clúster activo: helm template release-name ./chart -f values-override.yaml fallará con errores de schema si los valores son inválidos, y emitirá los manifiestos renderizados si son válidos. Pasar la salida a kubectl apply --dry-run=client -f - añade una capa adicional de validación de la API de Kubernetes para una comprobación offline exhaustiva.

¿Debo añadir values.schema.json a los charts que no mantengo (charts upstream)?

Para los charts upstream que consumís pero no mantenéis (como los charts de Bitnami, ingress-nginx, cert-manager), el enfoque recomendado es mantener un fichero JSON Schema separado en vuestro propio repositorio GitOps que valide vuestros ficheros de sobreescritura de valores específicos. Herramientas como jsonschema (Python) o ajv (Node.js) pueden validar un fichero de valores YAML/JSON contra un schema en CI sin que Helm esté involucrado. Esto os proporciona validación del schema para vuestras sobreescrituras específicas del entorno sin necesidad de modificar las fuentes de charts upstream.

Conclusión

values.schema.json es una de las funcionalidades más infrautilizadas de Helm. Está disponible desde Helm 3.0.0, no requiere dependencias externas, y transforma los errores de configuración de fallos silenciosos en tiempo de ejecución en errores explícitos en tiempo de construcción.

El coste de adoptarlo es bajo: un fichero JSON por chart, mantenido junto al código del chart. El beneficio es sustancial: validación de tipos, campos obligatorios forzados, vocabularios controlados, autocompletado en el IDE, y una puerta de CI que rechaza valores incorrectos antes de que lleguen a un clúster.

Para los equipos de plataforma que mantienen charts internos consumidos por múltiples equipos, el schema actúa como documentación ejecutable — una especificación formal que no puede desincronizarse con el comportamiento real porque es el comportamiento real. Los comentarios en values.yaml se ignoran durante el despliegue; el schema de JSON no.

Si vuestros charts de Helm no tienen todavía un values.schema.json, el siguiente paso es claro: elegid el chart más crítico, escribid un schema que cubra sus campos más usados con additionalProperties: false en cada nivel, añadid helm lint a vuestro pipeline de CI, y ejecutad el lint contra el conjunto de valores de CI que usáis hoy. Los primeros errores que encontréis serán reales — problemas existentes que llevan tiempo latentes y que el schema acaba de hacer visibles.