Tags inmutables en Kubernetes: por qué deberías usarlos

Hello world 🙂
Hace poco me pasó algo que seguramente a más de uno le ha pasado en Kubernetes: tenía una imagen correcta en el registry, el deployment apuntaba al tag correcto, pero los pods seguían arrancando con una versión vieja. Después de revisar Helm, ECR y hasta la arquitectura de la imagen, el problema terminó siendo más simple de lo que parecía: estaba usando imagePullPolicy: IfNotPresent y el nodo estaba reutilizando una imagen cacheada. Kubernetes usa IfNotPresent por defecto cuando la imagen no lleva :latest, y con esa política el kubelet reutiliza la imagen local si ya existe en el nodo.
Ese tipo de problema me hizo volver a un tema que muchas veces se subestima: la importancia de usar tags inmutables. Porque IfNotPresent no es malo en sí mismo; el verdadero problema aparece cuando lo combinamos con tags reutilizados, mutables o ambiguos como latest, stable o incluso v1.0.0 sobrescrito varias veces. Kubernetes recomienda evitar :latest en producción y explica que un digest identifica de forma única una imagen concreta, mientras que un tag puede cambiar y apuntar a otro contenido.
Qué son los tags inmutables y por qué importan
Un tag inmutable es, en la práctica, un tag que una vez publicado no se vuelve a reutilizar para otro contenido. Si hoy subes una imagen como v1.0.0-r1, ese tag debería seguir apuntando siempre al mismo binario. Si mañana corregís algo, no deberías volver a empujar otra imagen con ese mismo tag: deberías publicar v1.0.0-r2.
Esto no es solo una buena práctica “de orden”. En Amazon ECR incluso puedes habilitar tag immutability a nivel de repositorio para impedir que alguien sobrescriba un tag existente; cuando está activada, ECR devuelve un ImageTagAlreadyExistsException si intentás empujar una imagen con un tag ya usado.
La ventaja de trabajar así es enorme: te da trazabilidad, te simplifica los rollback, reduce la confusión durante incidentes y hace que el comportamiento de Kubernetes sea mucho más predecible.
Qué es un digest y por qué deberías conocerlo
Un digest es la huella única de una imagen de contenedor. Normalmente aparece como un hash sha256 y representa exactamente el contenido real de esa imagen. Docker lo define como un identificador criptográfico único e inmutable; a diferencia de un tag, no “se mueve”.
Por ejemplo, esto es un tag:
123456789012.dkr.ecr.us-east-1.amazonaws.com/mi-app:v1.0.0Y esto es una referencia por digest:
123456789012.dkr.ecr.us-east-1.amazonaws.com/mi-app@sha256:522d68b7076bd3d3ff9ed95f213189b5cd4fe4f558ece0c3d68bf9891b5811b9La diferencia práctica es muy simple: el tag es cómodo para humanos, pero el digest es exacto. Kubernetes explica que si quieres asegurarte de correr siempre la misma versión de imagen, referenciar por digest es la forma más precisa de fijarla.
A mí me gusta explicarlo así: el tag es el nombre de la caja; el digest es el número de serie exacto de esa caja.
Qué es imagePullPolicy en Kubernetes
imagePullPolicy define cuándo Kubernetes intenta descargar una imagen desde el registry al arrancar un contenedor. Kubernetes soporta tres valores: IfNotPresent, Always y Never. Además, si no lo defines, el comportamiento por defecto depende del tag: si usas:latest, el valor por defecto es Always; en la mayoría de los demás casos, el valor por defecto es IfNotPresent.
Esto es importante porque mucha gente piensa que el problema está en Helm, en ECR o en el pipeline, cuando en realidad el comportamiento que observan es exactamente el que Kubernetes prometió hacer.
IfNotPresent: ventajas y desventajas
IfNotPresent le dice a Kubernetes: “descargá la imagen solo si no está ya presente en el nodo”. Eso lo vuelve eficiente, especialmente si tienes imágenes pesadas, despliegues frecuentes o quieres evitar pulls innecesarios al registry. Kubernetes documenta que con IfNotPresent el kubelet usa preferentemente la copia local si la encuentra.
La ventaja es clara: menos tráfico, menos dependencia del registry y arranques más rápidos cuando la imagen ya está en caché.
La desventaja también es clara: si reutilizas el mismo tag para otra imagen, el nodo puede seguir usando una copia vieja. Eso fue exactamente lo que me pasó. Desde afuera parece que “todo está bien”, pero el runtime sigue arrancando el contenido antiguo porque para él la imagen ya existe localmente.
Por eso, mi conclusión práctica es esta: IfNotPresent funciona muy bien cuando usás tags inmutables. Si no usas tags inmutables, tarde o temprano te puede generar confusión.
Always: cuándo tiene sentido
Always fuerza al kubelet a consultar el registry cada vez que crea el contenedor. Kubernetes explica que eso no necesariamente implica descargar todas las capas de nuevo, porque si el runtime ya tiene capas coincidentes puede reutilizarlas; pero sí obliga a resolver la referencia en el registry.
La gran ventaja de Always es que reduce el riesgo de correr una imagen local vieja cuando estás reutilizando tags. También tiene sentido en ciertos escenarios de seguridad o multi-tenant; de hecho, Kubernetes tiene el admission controller AlwaysPullImages, orientado a reforzar que las imágenes privadas siempre requieran autorización de pull en vez de depender de una copia local ya existente en el nodo.
La desventaja es que dependes más del registry y agregas una verificación extra en cada arranque. No siempre es un problema, pero existe.
Never: el menos usado
Never le dice a Kubernetes que no intente descargar la imagen. Si la imagen no está presente en el nodo, el pod falla. Kubernetes lo documenta de forma explícita.
Es útil en laboratorios muy controlados o entornos donde las imágenes ya vienen precargadas en los nodos, pero para el día a día en EKS no suele ser la opción más práctica.
Entonces, ¿qué conviene usar?
Mi recomendación personal, después de varios despliegues y algunos dolores de cabeza, sería esta:
Para producción, me quedo con tags inmutables + IfNotPresent.
Para entornos donde todavía reutilizas tags, Always puede ser una ayuda temporal.
Para casos muy específicos y controlados, Never puede tener sentido.
Y si quieres subir el nivel de consistencia todavía más, puedes desplegar por digest en vez de por tag, sobre todo en workloads críticos. Kubernetes y Docker coinciden en que el digest es la referencia más precisa de una imagen.
La lección que me dejó este problema
Después de pelear un rato con un caso real en EKS, terminé con una regla bastante simple:
si usás IfNotPresent, no reutilices tags jamás.
De hecho, si trabajás con Amazon ECR, mi consejo es habilitar tag immutability en repositorios importantes. Así evitás que alguien sobrescriba un tag por error y reducís muchísimo la probabilidad de despliegues inconsistentes. AWS incluso tiene una regla administrada de AWS Config para comprobar si la inmutabilidad de tags está habilitada en repositorios privados de ECR.
Ejemplo recomendado
Yo hoy prefiero algo así:
image: repository: 123456789012.dkr.ecr.us-east-1.amazonaws.com/mi-app tag: v1.0.0-r1 pullPolicy: IfNotPresentY si cambia algo, no vuelvo a usar ese mismo tag. Publico uno nuevo:
image: repository: 123456789012.dkr.ecr.us-east-1.amazonaws.com/mi-app tag: v1.0.0-r2 pullPolicy: IfNotPresentConclusión
imagePullPolicy parece un detalle chico, pero tiene impacto directo en la velocidad, la trazabilidad y la confiabilidad de tus despliegues. IfNotPresent puede ser una excelente política, siempre que la acompañes de una estrategia seria de versionado. Los tags mutables son cómodos al principio, pero a la larga generan dudas, inconsistencias y tiempo perdido en troubleshooting. Kubernetes documenta claramente el comportamiento de IfNotPresent, Always y Never, y AWS ECR incluso te permite reforzar la práctica de tags inmutables desde el propio registry.
Mi recomendación final es simple: usa tags inmutables, entiende qué es un digest y evita que el nombre de una imagen te mienta sobre el contenido real que estás ejecutando.

