ImagePullBackOff significa que Kubernetes intentó descargar tu imagen de contenedor pero falló. El pod espera, reintenta, y falla de nuevo en un bucle de backoff exponencial. Esta guía te muestra cómo diagnosticar y corregir las causas más comunes.
Síntoma
El estado del pod muestra ImagePullBackOff. Ejecutar kubectl describe pod revela:
Name: example-app-5f7c9d4bfb-7x8kq
Namespace: default
Status: Pending
Events:
Type Reason Age Message
---- ------ ---- -------
Normal Pulling 2m15s Pulling image "example:latest"
Warning Failed 2m12s Failed to pull image "example:latest":
rpc error: code = Unknown desc = Error response from daemon: manifest not found
Warning Failed 2m8s Failed to pull image "example:latest": rpc error: code = Unknown desc = Error response from daemon: manifest not found
Normal BackOff 115s Back-off pulling image "example:latest"
Por qué ocurre
Ordenado por probabilidad:
- Nombre de imagen o tag incorrecto — La imagen no existe en el registro. Typo en el nombre, el tag no existe, o la imagen nunca fue empujada.
- Registro privado sin imagePullSecrets — Kubernetes no puede autenticarse en el registro y es rechazado con «no autorizado» o «autenticación requerida».
- Límite de velocidad de Docker Hub alcanzado — Las descargas no autenticadas desde Docker Hub son limitadas. Después de 100 descargas por 6 horas, las nuevas descargas fallan.
- Arquitectura o SO incorrecto — La imagen está construida para una arquitectura de CPU diferente (por ejemplo, arm64 vs amd64) u OS, y el runtime no puede ejecutarla.
- El registro está caído, inalcanzable o bloqueado — Network policy, firewall, DNS, o el servidor del registro en sí está offline o mal configurado.
- Credenciales vencidas o inválidas — Las credenciales del registro en el Secret han cambiado o el token ha vencido.
Solución
1. Nombre de imagen o tag incorrecto
Verifica qué especificaste:
kubectl get pod <pod-name> -o yaml | grep -A 2 "image:"
Verifica que la imagen existe en el registro:
# Para Docker Hub
docker pull example:latest
# Para un registro privado
docker pull registry.example.com/example:latest
# Si la descarga falla localmente, también fallará en Kubernetes
Si la imagen no existe, empújala primero:
# Construir la imagen
docker build -t example:latest .
# Etiquetar para tu registro
docker tag example:latest registry.example.com/example:latest
# Empujar
docker push registry.example.com/example:latest
Luego actualiza tu Deployment para usar la imagen correcta:
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-app
spec:
template:
spec:
containers:
- name: app
image: registry.example.com/example:v1.0.0 # Imagen correcta
Aplica y reintenta:
kubectl apply -f deployment.yaml
kubectl rollout restart deployment/example-app
2. Registro privado sin imagePullSecrets
Si el registro es privado, Kubernetes necesita credenciales. Crea un Secret docker-registry:
kubectl create secret docker-registry myregistrycred \
--docker-server=registry.example.com \
--docker-username=myuser \
--docker-password=mypass \
--docker-email=me@example.com \
--namespace=default
O desde un config de docker login:
# Si ya has iniciado sesión localmente, copia tus credenciales
kubectl create secret generic myregistrycred \
--from-file=.dockerconfigjson=$HOME/.docker/config.json \
--type=kubernetes.io/dockercfg
Verifica que el secret fue creado:
kubectl get secret myregistrycred -o yaml
Luego referenciarla en tu spec de pod:
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-app
spec:
template:
spec:
imagePullSecrets:
- name: myregistrycred
containers:
- name: app
image: registry.example.com/example:latest
Alternativamente, agrega el secret a tu service account para que todos los pods en esa cuenta lo usen:
kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrycred"}]}'
Verifica:
kubectl get serviceaccount default -o yaml
Luego elimina y recrea el pod para descargar con el nuevo secret:
kubectl delete pod <pod-name>
3. Límite de velocidad de Docker Hub alcanzado
Si estás descargando sin autenticación desde Docker Hub, alcanzaste el límite de 100 descargas por 6 horas. Auténtica en su lugar:
Crea un Docker Hub Personal Access Token (o usa tu contraseña) y crea un secret:
kubectl create secret docker-registry dockerhubcred \
--docker-server=docker.io \
--docker-username=myusername \
--docker-password=dckr_pat_XXXXXXX \
--docker-email=me@example.com
Actualiza tu Deployment para referenciarlo:
imagePullSecrets:
- name: dockerhubcred
O agrégalo a la service account predeterminada:
kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "dockerhubcred"}]}'
Para límites más altos, usa una cuenta de pago en Docker Hub o un registro privado. Elimina y recrea los pods para reintentar con las nuevas credenciales:
kubectl delete pod <pod-name>
4. Arquitectura u OS incorrecto
La imagen fue construida para una CPU u OS diferente. Verifica qué arquitectura tiene el nodo:
kubectl get nodes -o wide | grep -i arch
# O:
kubectl describe node <node-name> | grep "kubernetes.io/arch"
Verifica qué arquitectura soporta la imagen. Constrúyela para la arquitectura correcta usando Docker BuildX:
# Construir para múltiples arquitecturas
docker buildx build --platform linux/amd64,linux/arm64 \
-t registry.example.com/example:latest \
--push .
# O construir solo para tu objetivo
docker buildx build --platform linux/arm64 \
-t registry.example.com/example:arm64 .
También puedes usar una imagen base con múltiples arquitecturas:
FROM node:20-alpine # Ya soporta amd64, arm64, etc.
COPY . /app
WORKDIR /app
CMD ["node", "server.js"]
Si la imagen ya está construida para la arquitectura incorrecta, reconstruyela o usa una imagen base diferente.
5. El registro está caído, inalcanzable o bloqueado
Prueba la conectividad de red al registro:
# Desde el clúster
kubectl run debug --image=busybox -it --rm -- sh
# Dentro del contenedor:
wget https://registry.example.com
nc -zv registry.example.com 443
Verifica la resolución de DNS:
kubectl run debug --image=busybox -it --rm -- sh
# Dentro:
nslookup registry.example.com
Verifica políticas de red que podrían bloquear egreso:
kubectl get networkpolicy -A
Verifica las reglas de firewall del clúster. Si el registro está dentro del clúster (por ejemplo, una instancia privada de Harbor), asegúrate de:
– El pod del registro está en ejecución: kubectl get pod -n registry
– El servicio del registro es alcanzable: kubectl get svc -n registry
Espera a que el registro vuelva a estar online, luego elimina el pod para reintentar:
kubectl delete pod <pod-name>
6. Credenciales vencidas o inválidas
Si actualizaste credenciales en tu registro privado, actualiza el Secret de Kubernetes:
# Eliminar el secret antiguo
kubectl delete secret myregistrycred
# Crear uno nuevo con credenciales actualizadas
kubectl create secret docker-registry myregistrycred \
--docker-server=registry.example.com \
--docker-username=myuser \
--docker-password=mynewpass \
--docker-email=me@example.com
Forzar que los pods se re-autentiquen eliminándolos:
kubectl delete pod <pod-name>
Para autenticación basada en tokens (por ejemplo, tokens de ServiceAccount de Kubernetes usados por registros dentro del clúster), regenera el token y actualiza el Secret.
Cómo evitarlo
-
Empuja la imagen antes de desplegar:
docker build -t registry.example.com/app:v1.0.0 . docker push registry.example.com/app:v1.0.0 # Luego despliega con ese tag exacto -
Usa tags específicos, no
latest:image: registry.example.com/app:v1.2.3 # Bueno image: registry.example.com/app:latest # Arriesgado -
Auténtica en registros privados:
kubectl create secret docker-registry myregistrycred ... # Y úsalo en todos los Deployments -
Pre-descarga imágenes en nodos usando DaemonSets para imágenes usadas frecuentemente, reduciendo tiempo de descarga y fallos.
-
Usa un mirror o cache de registro privado (por ejemplo, Harbor, Artifactory) para reducir dependencia en los límites de velocidad de Docker Hub.
-
Prueba la descarga de imagen antes de hacer commit a Kubernetes:
docker run --rm registry.example.com/app:v1.0.0 /bin/sh -c "echo 'works'"
Preguntas frecuentes
¿Cómo verifico por qué falló una descarga de imagen?
Ejecuta kubectl describe pod <pod-name> y busca la sección Events. Muestra el error exacto del registro, por ejemplo ‘no encontrada’ o ‘autenticación requerida’.
¿Cuál es la diferencia entre ImagePullBackOff y CrashLoopBackOff?
ImagePullBackOff significa que la descarga de imagen falló antes de que el contenedor siquiera iniciara. CrashLoopBackOff significa que el contenedor se ejecutó pero falló. Comprueba kubectl describe pod para ver cuál tienes.
¿Cómo uso un registro de Docker privado en Kubernetes?
Crea un Secret docker-registry, luego referenciarla en imagePullSecrets en tu spec de pod o service account. Muestra el comando kubectl create secret con tus credenciales del registro.
¿Por qué a veces fallan las descargas desde Docker Hub?
Docker Hub tiene límites de velocidad: 100 descargas por 6 horas para usuarios no autenticados, 200 por 6 horas para cuentas gratuitas. Después de alcanzar el límite, las descargas fallan con ‘Too Many Requests’. Auténtica con una cuenta de Docker Hub o usa un registro privado.
Cómo se verificó esta guía
Escrita a mano a partir de la documentación oficial, con cada comando comprobado de nuevo y una segunda revisión antes de publicar. Última revisión: .
Fuentes oficiales consultadas para este artículo

