1. Read the actual error
kubectl describe pod <pod> -n <ns> | sed -n '/Events/,$p'
| Event text | Cause |
|---|---|
manifest unknown / not found | Wrong repository or tag |
401 Unauthorized / authentication required | The cluster isn't allowed to pull |
403 Forbidden / denied: client with IP ... is not allowed access | ACR firewall or private endpoint rule |
dial tcp ... i/o timeout / no such host | Network or DNS from the nodes to ACR |
toomanyrequests | Docker Hub rate limit, not ACR. Import the image into ACR. |
2. Run the built-in check
az aks check-acr -g rg-aks -n aks-prod --acr myacr.azurecr.io
It tests authentication, DNS and connectivity from a node and reports what's broken.
3. Attach ACR (grants AcrPull to the kubelet identity)
az aks update -g rg-aks -n aks-prod --attach-acr myacr
To check it by hand:
KUBELET_ID=$(az aks show -g rg-aks -n aks-prod --query "identityProfile.kubeletidentity.objectId" -o tsv)
az role assignment list --assignee $KUBELET_ID --all --query "[?roleDefinitionName=='AcrPull'].scope" -o tsv
Note
The kubelet identity pulls images, not the cluster's control-plane identity. Granting AcrPull to the wrong one is a classic mistake.
4. The image name and tag
az acr repository show-tags -n myacr --repository payments/api --orderby time_desc --top 5 -o table
kubectl get pod <pod> -n <ns> -o jsonpath='{.spec.containers[*].image}'
Check the registry name in the image reference (myacr.azurecr.io/...). A missing domain makes the node try Docker Hub.
5. Networking (Premium with a private endpoint)
privatelink.azurecr.iomust be linked to the AKS VNet, and both the registry endpoint and the regional data endpoint (myacr.<region>.data.azurecr.io) must resolve to private IPs.- If public access is disabled, nodes can only pull through the private endpoint.
- An egress firewall must allow the ACR login server and data endpoints, plus
mcr.microsoft.comfor system images.
kubectl run dns-test --rm -it --image=busybox:1.36 --restart=Never -- nslookup myacr.azurecr.io
6. Pull secrets across tenants
When ACR is in another tenant and managed identity can't be used, create an imagePullSecret from a token or service principal, and reference it in the pod spec or on the service account.
Prevent it next time
- Pin image digests or immutable tags in production. Rewriting
:latestbreaks rollbacks. - Import public base images into ACR (
az acr import) to avoid Docker Hub limits and outages.