KYAML en K8s 1.37: freno al bug de Noruega en YAML
En pocas palabras: KYAML, estabilizado en Kubernetes 1.37 (Garhwal, 26/08/2026) según el KEP-5295 de SIG CLI, cierra el “Norway bug” al forzar comillas en todos los strings. Un test contra un kube-apiserver 1.37.0 confirmó que valores null o ~ igual crean el objeto pero borran la clave sin errores ni logs. Lo que sigue no es solo el resumen de ese test: es un intento de bajarlo a la pregunta que importa en la práctica, que es cuándo conviene molestarse en adoptar kyaml y cuándo no.
Kubernetes 1.37, lanzado el 26 de agosto de 2026 bajo el nombre Garhwal, llevó KYAML a estable según el anuncio oficial del release. Una prueba publicada contra un kube-apiserver 1.37.0 real muestra que un ConfigMap con un campo en null o ~ se crea sin ningún error, pero la clave desaparece del objeto sin dejar rastro en ningún log.
KYAML es el formato de salida de kubectl, estabilizado en Kubernetes 1.37 mediante el KEP-5295, que representa manifiestos en un subconjunto más estricto de YAML: entrecomilla todos los valores de tipo string, usa llaves para mapas y corchetes para listas. SIG CLI lo desarrolló para eliminar el bug de Noruega en YAML Kubernetes, donde un valor como NO se interpreta como booleano en vez de quedar como texto plano.
En este artículo:
- En 30 segundos
- ¿Qué es el bug de Noruega en YAML Kubernetes y por qué sigue vivo en la 1.37?
- ¿Cómo borra datos kubectl apply sin mostrar ningún error?
- ¿Por qué –dry-run=server no detecta el bug del campo faltante?
- ¿Qué es KYAML y cómo llega a estable en Kubernetes 1.37?
- ¿KYAML funciona con cualquier versión de kubectl aunque no la reconozca?
- ¿Cuánto pesa más un manifiesto en formato kyaml frente a yaml y json?
- Tres preguntas para decidir si migrar a kyaml ahora tiene sentido
- ¿Cómo revisar manifiestos existentes para evitar este bug hoy mismo?
- Qué está confirmado y qué no
- Errores comunes al lidiar con este bug
- Preguntas frecuentes
- Conclusión
- Fuentes
En 30 segundos
- Kubernetes 1.37 (Garhwal, 26/08/2026) estabilizó KYAML según el KEP-5295 de SIG CLI, dentro de un total de 67 mejoras con 16 features en estable.
- Un test contra un kube-apiserver 1.37.0 real mostró que un ConfigMap con
field: ~ofield: nullse crea igual, pero pierde la clavedatasin ningún error. --dry-run=serverdetecta los valores rechazados por tipo (booleanos y números) pero no avisa cuando la clave desaparece por null o tilde.- El código propio que usa
sigs.k8s.io/yamldirectamente (la base de client-go) corrompió 11 de 14 valores probados sin lanzar ningún error. - Un ConfigMap en formato kyaml pesa 693 bytes contra 548 en YAML plano, un 26% más, pero elimina la ambigüedad de tipos.
¿Qué es el bug de Noruega en YAML Kubernetes y por qué sigue vivo en la 1.37?
El bug de Noruega en YAML Kubernetes es la regla de YAML 1.1 que convierte valores sin comillas como NO, Yes, OFF o y en booleanos en lugar de dejarlos como texto, algo que el propio KEP-5295 cita como motivación central para crear KYAML. El nombre sale del código de país “NO” (Noruega), que cualquier parser YAML 1.1 estándar lee como false.
No es el único caso. El mismo KEP menciona números como _42, _4_2_ o 11:00 (interpretado en base 60) como ejemplos de valores que parecen texto y terminan siendo otra cosa. Esto es antecedente, no la noticia en sí: el bug se conoce desde hace años. Lo que cambia en Kubernetes 1.37 es que, según el propio blog del release, la herramienta para esquivarlo pasó de beta a estable con conformance testing completo.
¿Cómo borra datos kubectl apply sin mostrar ningún error?
Con null o ~ como valor de un campo, kubectl apply crea el objeto igual, pero la clave desaparece del data del ConfigMap sin ningún mensaje de error ni advertencia. Así lo documenta la prueba publicada en dev.to, corrida contra binarios reales de kube-apiserver 1.37.0 y etcd 3.5.17, sin kubelet ni containerd de por medio. Vale aclarar de entrada que se trata de una prueba de un tercero, no de algo que hayamos corrido nosotros: la reportamos con esa salvedad porque los resultados son consistentes con la mecánica que describe el propio KEP-5295 (un decoder tipado que no tiene forma de rechazar un valor nulo).
El autor de la prueba escribió trece ConfigMaps, cada uno con un valor ambiguo distinto, y los aplicó todos contra el mismo servidor en vivo:
| Valor escrito | Qué pasó al aplicarlo |
|---|---|
| country: NO | Rechazado (BadRequest) |
| flag: no | Rechazado |
| flag2: Yes | Rechazado |
| flag3: OFF | Rechazado |
| flag4: y | Rechazado |
| flag5: n | Rechazado |
| answer: true | Rechazado |
| answer2: False | Rechazado |
| version: 1.0 | Rechazado, leído como número |
| zero_padded: 010 | Rechazado, leído como número |
| nothing: null | Creado, pero sin la clave |
| tilde: ~ | Creado, pero sin la clave |
| plain: hello | Creado correctamente |

Los diez valores con forma de booleano o número chocan contra un error de tipo: ConfigMap.data es map[string]string en Go, y el decoder tira BadRequest antes de escribir nada, algo molesto pero seguro. El problema real está en null y ~: no son ni string ni booleano, así que no hay conflicto de tipo que rechazar. El decoder simplemente trata el valor nulo como “no hay nada acá” y borra la clave en silencio.
Ejemplo hipotético: un values.yaml de Helm con region: NO
Lo que sigue es un ejemplo hipotético para ilustrar el mecanismo ya descrito, no un caso real ni verificado por esta redacción. Pensemos en un equipo que mantiene un values.yaml de Helm con algo tan común como esto:
region: NO
featureFlags:
betaAccess: no
apiVersion: 1.0Si ese chart se renderiza y se aplica con helm template | kubectl apply -f -, tanto region: NO como betaAccess: no terminan siendo booleanos disfrazados dentro de campos que la API espera como string. Según la mecánica que describe la prueba de dev.to, el apiserver los va a rechazar con BadRequest: un fallo ruidoso que cualquier pipeline de CI mínimamente atento va a frenar antes de llegar a producción. El escenario que debería preocupar más es otro: si ese mismo values.yaml alimenta, en paralelo, un controlador propio que llama yaml.Unmarshal directo contra sigs.k8s.io/yaml —el mismo paquete que usa client-go— ahí no hay BadRequest que avise nada. no se convierte en la cadena "false" y 1.0 en "1", silenciosamente, y el controlador sigue operando con un dato equivocado. Es la misma mecánica documentada en la prueba, aplicada a un archivo que cualquier equipo con Helm en producción podría tener hoy en su repositorio sin haberlo revisado nunca.
¿Por qué –dry-run=server no detecta el bug del campo faltante?
--dry-run=server atrapa los diez rechazos por tipo (mismo BadRequest, ningún objeto creado) pero no atrapa el caso de null o ~. Con esos valores, el dry-run devuelve éxito y un YAML donde la clave ya está faltando, sin ninguna advertencia que lo señale. Esto se conecta con lo que analizamos en guía de migración a kyaml en Kubernetes.
Este es, para nosotros, el hallazgo más incómodo de toda la prueba: no porque el bug en sí sea nuevo, sino porque ataca justo la herramienta que un equipo de plataforma suele señalar como su red de seguridad. Un pipeline de CI que confía en el dry-run para frenar manifiestos rotos va a ver luz verde exactamente en el caso que más le importa: el que no tira error, corrompe el objeto y sigue de largo. Hay que saber, de antemano, que conviene contar campos manualmente para darse cuenta.
Ahora bien, hay algo peor que el propio apiserver. La decodificación estricta que rechaza booleanos y números vive en el apiserver, pero no en todo el ecosistema. client-go usa el mismo paquete sigs.k8s.io/yaml, y cualquier controlador o CLI que lo llame directo, sin pasar por el apiserver, no tiene esa protección. Un programa de diez líneas en Go que hace yaml.Unmarshal contra un map[string]string corrompió 11 de 14 valores sin devolver ningún error: NO pasó a "false", null pasó a "", y 010 terminó como "8" porque un parser de números interpretó el cero inicial como octal. Nada en un diff de código review avisa que ese cambio existe.
¿Qué es KYAML y cómo llega a estable en Kubernetes 1.37?
KYAML es un subconjunto de YAML que kubectl get -o kyaml puede imprimir y que kubectl apply -f acepta como entrada normal, porque sigue siendo YAML válido por debajo. El KEP-5295 entró como alpha en Kubernetes 1.34, pasó a beta en 1.35 y llegó a estable en la 1.37 con conformance testing completo, según confirma el blog oficial del release.
La receta es simple: entrecomillar siempre los valores string, usar {} para mapas y structs, usar [] para listas. A diferencia de JSON, KYAML permite comentarios y comas finales, y agrega un encabezado --- para distinguirse de un JSON mal formado a simple vista. Es exclusivamente client-side: el KEP aclara que no toca el apiserver ni ningún otro componente más allá de kubectl. Complementá con generar NetworkPolicies a partir de Hubble.
¿KYAML funciona con cualquier versión de kubectl aunque no la reconozca?
Sí, porque KYAML sigue siendo YAML normal por debajo, cualquier kubectl lo procesa sin necesitar el flag ni conocer el KEP. La prueba de dev.to armó a mano un manifiesto kyaml con comentarios y comas finales (ambas cosas prohibidas en JSON puro) y lo aplicó contra el servidor en vivo sin ningún reclamo: configmap/norway-commented created.
Eso confirma lo que dice el anuncio: la compatibilidad hacia atrás no es una promesa de marketing, es una consecuencia directa de que KYAML nunca deja de ser YAML. Subís el manifiesto, lo probás en un kubectl viejo, funciona igual, no hace falta actualizar nada en la cadena de CI para empezar a generarlo.
¿Cuánto pesa más un manifiesto en formato kyaml frente a yaml y json?
El mismo ConfigMap pesa 548 bytes en YAML plano, 693 en kyaml y 762 en JSON, según midió la prueba de dev.to. Eso es un 26% más de tamaño que el YAML convencional, aunque todavía queda por debajo de JSON. Para más detalles técnicos, mirá probar estos cambios en Online Boutique.
| Formato | Tamaño (bytes) |
|---|---|
| YAML (-o yaml) | 548 |
| KYAML (-o kyaml) | 693 (+26%) |
| JSON (-o json) | 762 |
Para un archivo que un humano lee de vez en cuando y una máquina reparsea todo el tiempo, ese costo de tamaño se paga solo con evitar un incidente de producción. El tema es que nadie te obliga a adoptarlo: sigue siendo opt-in, y no hay plan de hacerlo el formato por defecto.
Tres preguntas para decidir si migrar a kyaml ahora tiene sentido
El 26% extra de tamaño no es gratis, y no todo archivo lo justifica. En base a lo que confirman las fuentes citadas —quién decodifica cada tipo de dato, y con qué grado de protección— nos parece que la decisión se ordena mejor con tres preguntas que con una recomendación genérica de “usalo siempre”:
- ¿Quién edita ese archivo? Si el consumidor final es una persona tipeando a mano (values.yaml, ConfigMaps sueltos, secretos de configuración), el riesgo de escribir sin querer un valor ambiguo es alto todos los días, y ahí el costo de tamaño de kyaml se justifica solo.
- ¿Quién lo decodifica del otro lado? Si el archivo llega al apiserver vía
kubectl apply, el peor escenario para un booleano o número disfrazado es unBadRequestruidoso: molesto, pero seguro. Si en cambio lo consume un controlador propio que llamasigs.k8s.io/yamlsin pasar por el apiserver —el mismo path que usa client-go—, el riesgo pasa a ser corrupción silenciosa, y ese es el caso que amerita blindarse ya, con kyaml o al menos con comillas explícitas. - ¿Se regenera solo o se toca a mano de vez en cuando? Un archivo que una máquina reparsea constantemente amortiza rápido el costo extra de bytes. Uno que un humano edita una vez cada tanto quizás no lo justifique, y ahí alcanza con el hábito manual de comillas.
¿Cómo revisar manifiestos existentes para evitar este bug hoy mismo?
El consejo práctico que se desprende de la prueba es hacer grep en los manifiestos hand-edited (Helm values, ConfigMaps sueltos) buscando valores sin comillas como no, yes, on, off, null, ~, o números tipo 010 con ceros a la izquierda. El riesgo mayor no está en el apiserver, que al menos rechaza los booleanos con error; está en el código propio que llama sigs.k8s.io/yaml directamente, la misma librería que usa client-go.
Como criterio propio de verificación —y lo marcamos como tal, porque es una propuesta editorial que no vimos probada en ninguna de las fuentes ni verificamos nosotros mismos—: antes de mergear un cambio en valores de Helm o en un ConfigMap sensible, corré kubectl get -o yaml y kubectl get -o kyaml sobre el mismo objeto aplicado y comparás el número de claves en data. Si difieren, hay un valor que se perdió en el camino. No es una herramienta automática, es un chequeo manual mientras no exista algo mejor. Sobre eso hablamos en patrones de arquitectura recomendados en Kubernetes.
Qué está confirmado y qué no
- Confirmado: KYAML es estable en Kubernetes 1.37, según el anuncio oficial y el KEP-5295.
- Confirmado: el comportamiento silencioso con
nully~se reprodujo contra un kube-apiserver 1.37.0 real, corriendo directo sin kubelet, según la prueba de dev.to (no verificado de forma independiente por esta redacción). - Confirmado: KYAML es exclusivamente client-side, el KEP lo aclara explícitamente, no hay cambios en el apiserver para este KEP.
- Pendiente: si versiones de Kubernetes anteriores a la 1.37 se comportan igual con null/tilde; la prueba solo corrió contra 1.37.0.
- Pendiente: si herramientas como Helm o ArgoCD van a adoptar kyaml como salida por default en algún momento.
- Editorial, no verificado: el ejemplo de Helm con
region: NOy el criterio de comparar conteo de claves entre yaml/kyaml son ideas nuestras para ilustrar y operacionalizar el hallazgo, no pruebas de las fuentes citadas.
Errores comunes al lidiar con este bug
- Confiar en –dry-run=server como red de seguridad total. Atrapa los rechazos por tipo, pero no avisa cuando una clave desaparece por null o tilde. Hay que revisar el conteo de campos, no solo el código de salida.
- Usar sigs.k8s.io/yaml en código propio pensando que tiene la misma protección que el apiserver. El decoder tipado del apiserver rechaza los mismatches; una llamada directa a
yaml.Unmarshalcontra un mapa de strings no tiene ese filtro y corrompe el valor sin error. - Asumir que adoptar KYAML rompe los pipelines existentes. Sigue siendo YAML válido, cualquier kubectl lo acepta como entrada, y no hace falta tocar CI para empezar a generarlo con
-o kyaml. - No revisar manifiestos viejos antes de tocarlos. Un código de país, una versión de software o un flag booleano escritos hace tiempo pueden llevar años agazapados sin que nadie los haya cuestionado.
Preguntas frecuentes
¿Qué versión de Kubernetes estabilizó KYAML y kubectl apply sigue aceptando manifiestos viejos?
Kubernetes 1.37 (Garhwal, 26/08/2026) llevó KYAML de beta a estable, dentro de un total de 16 features graduadas en ese release. Y sí: KYAML no reemplaza nada, es un formato de salida opcional de kubectl. Los manifiestos existentes, las herramientas y los pipelines de CI siguen funcionando sin ningún cambio.
¿Un archivo escrito en kyaml funciona en un kubectl que no lo conoce?
Sí, porque un archivo kyaml es YAML válido por debajo. Cualquier versión de kubectl lo procesa sin reclamos, incluso una que nunca escuchó hablar del KEP-5295, tal como lo comprobó la prueba de dev.to aplicando un manifiesto kyaml con comentarios y comas finales contra un servidor en vivo.
¿–dry-run=server detecta los campos que se borran silenciosamente?
No en todos los casos. Detecta los valores rechazados por tipo, como booleanos o números, pero cuando el valor es null o ~ reporta éxito y muestra un objeto que ya perdió la clave, sin ninguna advertencia. Es, para nosotros, el dato más relevante de toda la prueba.
¿Cómo genero la salida en formato kyaml con kubectl?
Con kubectl get , disponible como formato estable desde Kubernetes 1.37. Para dry-run sin servidor, kubectl create configmap x --dry-run=client -o kyaml funciona incluso sin cluster.
Conclusión
Lo que cambió con Kubernetes 1.37 no es que el bug de Noruega en YAML Kubernetes haya desaparecido: sigue ahí, escrito en la especificación de YAML 1.1 desde siempre. Lo que cambió es que ahora existe una salida de kubectl, estable y probada con conformance testing, que lo esquiva por completo entrecomillando cada valor. El hallazgo más importante para cualquiera que administre clusters en producción no es ese, sino el que se esconde detrás: --dry-run=server no es la red de seguridad completa que muchos asumen, y el código propio que usa sigs.k8s.io/yaml directo tiene menos protección que el apiserver. La adopción de kyaml no tiene por qué ser todo o nada: si mantenés manifiestos hand-edited que un humano toca seguido, o código propio que decodifica YAML sin pasar por el apiserver, ahí es donde el costo de tamaño se paga solo. Si no es tu caso, el hábito más barato sigue siendo simplemente entrecomillar a mano lo que parece texto y no lo es.
Fuentes
- Kubernetes v1.37: Garhwal – anuncio oficial del release
- Kubernetes 1.37’s kyaml output closes a YAML bug that still deletes data on apply – prueba técnica en dev.to
- KEP-5295: KYAML – documento de diseño oficial
- How to Pretty-Print Your Kubernetes YAML as KYAML and Why You’d Want To – blog oficial de Kubernetes






