How to Fix Azure Container Registry Errors
Why Azure Container Registry Errors Keep Happening
You've built a container image, you run docker push, and , nothing. Either you get slapped with Error response from daemon: login attempt to https://yourregistry.azurecr.io/v2/ failed, or the dreaded UNAUTHORIZED: authentication required, or worse, a silent timeout that leaves you staring at a blinking cursor. I've seen this exact situation on dozens of Azure tenants, and trust me, it's almost never the image itself.
Azure Container Registry is Microsoft's managed, private registry service built on the open-source Docker platform. It lets you store, build, and manage container images and related artifacts, everything from standard Docker images to Helm charts and OCI (Open Container Initiative) format artifacts. It integrates directly with Azure Kubernetes Service (AKS), Azure App Service, Azure Batch, and your CI/CD pipelines in Azure Pipelines or Jenkins. It sounds simple. In practice, it has more failure modes than most Azure services, and Microsoft's error messages rarely tell you which one you hit.
There are a handful of root causes that account for roughly 90% of Azure Container Registry problems people report:
- Stale or missing authentication tokens. The
az acr logincommand issues a time-limited credential. Once it expires, usually after 3 hours, every push and pull returns a 401. - Wrong or missing RBAC role assignment. Even if you own the subscription, you still need the right Azure role assigned to your identity on the specific registry resource. AcrPush, AcrPull, and AcrDelete are the granular roles. Without them, you get access denied.
- SKU limitations. If you're on the Basic tier and hitting storage or throughput walls, Azure silently throttles you. The Basic tier is designed for development scenarios, not production image distribution across regions.
- Network-level blocks. Firewalls, private endpoints misconfigured, or VNet rules blocking the registry endpoint are extremely common in enterprise environments.
- Azure CLI version mismatch. Commands introduced after version 2.0.55 don't exist in older CLI builds. This causes cryptic failures that look like auth problems but are actually missing sub-commands.
What makes this particularly frustrating is that Microsoft surfaces the same UNAUTHORIZED error for completely different underlying problems, expired tokens, missing roles, and network blocks all look identical in the Docker CLI output. That's why the fix isn't just "log in again." You need to systematically eliminate each possible cause. I'll walk you through exactly that. Browse all Microsoft fix guides →
The Quick Fix, Try This First
Before you spend an hour digging through RBAC assignments and firewall rules, try this. It resolves about 60% of Azure Container Registry login and push failures in under two minutes.
Open your terminal and run these three commands in order:
# Step 1: Make sure your CLI is current
az upgrade
# Step 2: Re-authenticate to Azure
az login
# Step 3: Log directly into your registry
az acr login --name YOUR_REGISTRY_NAME
Replace YOUR_REGISTRY_NAME with just the registry name, not the full URL. So if your registry is mycompanyregistry.azurecr.io, the command is az acr login --name mycompanyregistry. Don't include .azurecr.io. That's a very common mistake.
After running az acr login, you should see Login Succeeded in the terminal. If you see that, immediately try your push or pull again. Nine times out of ten, the original token had simply expired and this refresh fixes everything.
If you get The command az acr login could not be found, your Azure CLI is outdated. Run az upgrade first and confirm you're on version 2.0.55 or later with az --version.
If you see ERROR: Failed to connect to MSI. Please make sure MSI is configured correctly, you're running this inside an environment configured for Managed Service Identity but MSI isn't set up. Jump ahead to Step 2 for the Managed Identity fix.
If none of the above, Login Succeeded appeared but push still fails, you have an RBAC problem, not an auth problem. Keep reading.
az acr check-health --name YOUR_REGISTRY_NAME --yes before anything else. This single command checks your Docker daemon, your CLI version, your registry connectivity, and your authentication all in one shot. It surfaces the exact failure category, which cuts your debugging time in half. Most people don't know it exists.
This is the boring first step that everyone skips, and then wastes 40 minutes on. Check it first.
az --version
You need version 2.0.55 or later. If you're below that, the az acr sub-commands may be incomplete or missing entirely, which produces errors that look exactly like authentication failures but aren't. Update with:
# Windows (via MSI installer, reopen terminal after)
az upgrade
# Or on Windows via winget
winget upgrade Microsoft.AzureCLI
Once you've confirmed your CLI version, run the health check against your specific registry:
az acr check-health --name YOUR_REGISTRY_NAME --yes
The --yes flag skips the interactive confirmation prompt, which matters in scripts. This command will output a table of checks. Look specifically for:
- DOCKER_PULL_ERROR, Docker isn't running or can't pull the hello-world test image
- CONNECTIVITY_DNS_ERROR, DNS can't resolve your registry's hostname
- CONNECTIVITY_ENDPOINT_ERROR, The registry endpoint is unreachable (firewall or private endpoint issue)
- HELM_VERSION_ERROR, Only matters if you're storing Helm charts
If the health check shows green across the board but you still can't push, the problem is definitively RBAC. Move to Step 3. If you see CONNECTIVITY_DNS_ERROR, your machine can't resolve the Azure Container Registry endpoint, that's a local DNS or VPN issue, and Step 4 covers it. If Docker shows errors, make sure the Docker daemon is running before anything else: open Docker Desktop and confirm it shows "Engine running."
What success looks like: Every line in the health check table shows OK or a non-blocking warning. You can proceed to test a simple pull with docker pull mcr.microsoft.com/hello-world to confirm Docker itself is functional.
Azure Container Registry uses Microsoft Entra ID (formerly Azure Active Directory) as its primary authentication layer. When something in that chain breaks, expired tokens, wrong tenant, or a conditional access policy blocking the request, you get 401 Unauthorized or UNAUTHORIZED: authentication required.
First, make sure you're logged into the right tenant:
# See which account and tenant you're currently using
az account show
# If you need to switch tenants, log out and back in with the tenant ID
az logout
az login --tenant YOUR_TENANT_ID
If your organization has conditional access policies that restrict access to Azure resources from unmanaged devices or specific IP ranges, your login may succeed at the CLI level but fail at the registry level. You'll see this as a 401 that immediately follows a "Login Succeeded" message, confusing but explainable. In that case, your IT admin needs to check the conditional access policies in the Microsoft Entra admin center under Protection > Conditional Access > Policies.
For environments that use Managed Identity, common in AKS clusters and Azure VMs, don't use az acr login at all. Instead, authenticate via the managed identity directly:
# Authenticate using system-assigned managed identity
az login --identity
# Then log into the registry
az acr login --name YOUR_REGISTRY_NAME
If you're using a service principal (common in CI/CD pipelines), the login syntax is different:
docker login YOUR_REGISTRY_NAME.azurecr.io \
--username YOUR_SERVICE_PRINCIPAL_APP_ID \
--password YOUR_SERVICE_PRINCIPAL_PASSWORD
One mistake I see constantly: people pass the service principal's display name as the username instead of the Application (client) ID. The client ID is the GUID, find it in the Azure portal under Microsoft Entra ID > App registrations > [your app] > Overview. Use that GUID, not the name.
What success looks like: docker login or az acr login returns Login Succeeded with no error codes. You can then attempt a push or pull and it completes without a 401.
This is the most common misconfiguration I see in enterprise Azure setups. You can be a subscription Owner and still fail to push images if you haven't been explicitly assigned a registry-level role. Azure Container Registry uses Azure role-based access control (RBAC) for fine-grained permissions, and the standard subscription roles (Owner, Contributor, Reader) don't automatically grant the container-specific operations.
The three most important ACR-specific roles are:
- AcrPull, pull images from the registry
- AcrPush, push and pull images
- AcrDelete, delete images and tags
Check what roles your current identity has on the registry:
az role assignment list \
--assignee YOUR_USER_OR_SP_OBJECT_ID \
--scope $(az acr show --name YOUR_REGISTRY_NAME --query id --output tsv)
If the output is empty or doesn't include AcrPush/AcrPull, assign the right role:
# Assign AcrPush to a user
az role assignment create \
--assignee USER_EMAIL_OR_OBJECT_ID \
--role AcrPush \
--scope $(az acr show --name YOUR_REGISTRY_NAME --query id --output tsv)
# Assign AcrPull to a service principal
az role assignment create \
--assignee SERVICE_PRINCIPAL_APP_ID \
--role AcrPull \
--scope $(az acr show --name YOUR_REGISTRY_NAME --query id --output tsv)
Role assignments can take 2–5 minutes to propagate across Azure's authorization system. Don't test immediately, wait a few minutes, then run az acr login --name YOUR_REGISTRY_NAME again and retry your push or pull.
You can also assign roles through the Azure portal: navigate to your registry resource > Access control (IAM) > Add role assignment. Select the role, then search for your user, group, or service principal in the Members tab.
What success looks like: The az role assignment list command shows AcrPush or AcrPull in the output. After waiting a few minutes, your image push completes without an access denied error.
Even with perfect authentication and RBAC, you'll hit denied: requested access to the resource is denied or repository does not exist or may require 'docker login' if your image isn't tagged for your specific registry. This trips up people who are used to pushing to Docker Hub.
For Azure Container Registry, every image tag must include the full registry login server as the prefix. The format is:
YOUR_REGISTRY_NAME.azurecr.io/REPOSITORY_NAME:TAG
Here's the full workflow for pushing an image:
# Get your registry's login server (in case you're unsure)
az acr show --name YOUR_REGISTRY_NAME --query loginServer --output tsv
# Tag your local image for the registry
docker tag local-image-name:latest YOUR_REGISTRY_NAME.azurecr.io/myapp:v1.0
# Log in (if you haven't already)
az acr login --name YOUR_REGISTRY_NAME
# Push the tagged image
docker push YOUR_REGISTRY_NAME.azurecr.io/myapp:v1.0
And for pulling:
docker pull YOUR_REGISTRY_NAME.azurecr.io/myapp:v1.0
A common mistake: people tag with just the repository name (e.g., myapp:v1.0) and then try to push, Docker tries to push to Docker Hub instead of your ACR instance. Always include the full login server prefix.
To verify that your image actually made it into the registry, list the repositories:
az acr repository list --name YOUR_REGISTRY_NAME --output table
And to see the specific tags for a repository:
az acr repository show-tags --name YOUR_REGISTRY_NAME --repository myapp --output table
If you need to import an image from Docker Hub or another public registry into your ACR without pulling it locally first, very useful for air-gapped or bandwidth-constrained environments, the import command handles this entirely server-side:
az acr import \
--name YOUR_REGISTRY_NAME \
--source docker.io/library/nginx:latest \
--image nginx:latest
What success looks like: az acr repository list shows your repository in the list. The tag appears when you run show-tags. Your deployment pipeline can now pull the image by its full ACR tag.
If you're setting up Azure Container Registry for the first time and hitting errors before you even get to pushing images, the problem is almost always in the creation step. Registry names must be globally unique across all of Azure, must be 5–50 characters long, and can only contain alphanumeric characters, no hyphens, no underscores. That last part surprises a lot of people.
Here's the correct creation workflow via Azure CLI:
# Create a resource group first (skip if you have one)
az group create --name myResourceGroup --location eastus
# Create the registry, choose your SKU carefully (Basic, Standard, or Premium)
az acr create \
--resource-group myResourceGroup \
--name mycompanyregistry \
--sku Standard
On SKU selection: Basic is fine for personal development and testing. It's not for production. Standard gives you more storage and higher throughput, and it's the right default for small to mid-size teams. Premium unlocks geo-replication, private endpoints, content trust for image signing, and significantly higher storage limits, you need Premium for any multi-region deployment or enterprise compliance scenario.
After creation, verify the registry is up and get its login server:
az acr show --name mycompanyregistry --query loginServer --output tsv
This outputs something like mycompanyregistry.azurecr.io, that's the address you'll use in all your docker tag and docker push commands going forward.
To create the registry through the Azure portal instead: go to portal.azure.com > search for "Container registries" in the top search bar > click Create. Fill in your subscription, resource group, registry name, location, and SKU. Under the Networking tab, decide whether you need public access or private endpoint access (Premium only). Then hit Review + create and wait about 30 seconds for provisioning.
If you get The registry name is already taken, the name is globally reserved, try a longer, more unique name. If you get The subscription does not have enough quota to create this resource, you've hit your subscription's registry count limit, open a support request to increase it.
What success looks like: The az acr show command returns your registry's details including the login server URL. The registry appears in the Azure portal under Container registries in your resource group.
Advanced Troubleshooting for Azure Container Registry
Diagnosing Network-Level Failures in Enterprise Environments
If your organization uses private endpoints, VNet service endpoints, or IP allowlisting, and many do, then registry connectivity failures often have nothing to do with your credentials. The Docker client and Azure CLI both time out silently when a firewall drops the packet, and the error output looks identical to an auth failure.
First, test raw connectivity to the registry endpoint:
# Test DNS resolution
nslookup YOUR_REGISTRY_NAME.azurecr.io
# Test HTTPS connectivity on port 443
curl -v https://YOUR_REGISTRY_NAME.azurecr.io/v2/
If nslookup returns a private IP address (in the 10.x.x.x or 172.x.x.x range), your registry is configured with a private endpoint and you must be on the correct VNet or connected via VPN/ExpressRoute. If it returns a public Azure IP but your registry has public network access disabled, you're blocked at the firewall level, your network admin needs to add your outbound IP to the registry's IP allowlist.
To check and update the registry's network rules via CLI:
# View current network rules
az acr show --name YOUR_REGISTRY_NAME --query networkRuleSet
# Add an IP rule (Premium SKU only)
az acr network-rule add \
--name YOUR_REGISTRY_NAME \
--ip-address YOUR_PUBLIC_IP/32
Analyzing Azure Activity Logs for ACR Failures
Azure Container Registry doesn't have a traditional Event Viewer, but the Azure Activity Log is its equivalent. When a push or authentication fails at the platform level, the error shows up here with full detail that Docker CLI never surfaces.
In the Azure portal: navigate to your registry > Activity log in the left menu. Filter by Operation: Push Image or Operation: Login and set the time range to the last hour. Failed operations show up in red with a Status: Failed badge. Click any entry to see the full JSON payload, it includes the caller identity, the exact error code, and the timestamp.
For scripted log analysis:
az monitor activity-log list \
--resource-group myResourceGroup \
--resource-type Microsoft.ContainerRegistry/registries \
--start-time 2026-04-20T00:00:00Z \
--query "[?status.value=='Failed']" \
--output table
Fixing ACR Tasks Build Failures
If you're using ACR Tasks to automate image builds, triggered by Git commits, base image updates, or schedules, build failures appear in the task run logs, not in your local terminal. Check them with:
# List recent task runs
az acr task list-runs --name YOUR_REGISTRY_NAME --output table
# Get logs for a specific run
az acr task logs --name YOUR_REGISTRY_NAME --run-id YOUR_RUN_ID
The most common ACR Tasks failure is a missing or expired GitHub personal access token for the source code trigger. The token needs repo scope. Update it in the task definition under Authentication in the Azure portal, or via az acr task update --name TASK_NAME --registry YOUR_REGISTRY_NAME --git-access-token NEW_TOKEN.
Escalate to Microsoft Support when: (1) the registry health check passes but pushes fail with HTTP 500 errors, that's a platform-side issue Microsoft needs to investigate. (2) Geo-replication between regions is stuck in a "Pending" or "Failed" state for more than 30 minutes. (3) You've confirmed your RBAC assignments, network rules, and tokens are correct, but still get 401s, this sometimes indicates a Managed Identity misconfiguration at the platform level that requires Azure support access to diagnose. Open a support ticket at severity B (moderate business impact), ACR issues qualify and typically get a response within 4 hours on Business plans.
Prevention & Best Practices for Azure Container Registry
Most Azure Container Registry problems are preventable. Once you've fixed the immediate issue, these practices will keep you from seeing it again.
Choose the Right SKU from the Start
Upgrading a registry from Basic to Standard or Premium is straightforward, you just update the SKU and your data moves automatically. But running a production workload on Basic and then scrambling to upgrade when you hit storage limits or need geo-replication is stressful. If you're building anything customer-facing or multi-region, start with Standard at minimum. If you need content trust for image signing, private endpoints, or cross-region replication, start with Premium.
Use Managed Identity Instead of Service Principals in CI/CD
Service principal credentials expire. Managed identities don't. If your build pipelines run inside Azure, in AKS, Azure DevOps agent pools, GitHub Actions with Azure, or Azure Container Apps, assign a managed identity to that compute resource and grant it AcrPush. You eliminate an entire class of "pipeline suddenly broke at 2am because the service principal password expired" incidents.
Automate Image Lifecycle with Retention Policies
Unmanaged registries fill up. Every CI build that pushes an image with a unique tag (like a commit SHA) adds to your storage bill. Set up a retention policy to auto-purge untagged manifests:
# Purge images older than 30 days with no tags, as a scheduled ACR Task
az acr task create \
--name purge-old-images \
--registry YOUR_REGISTRY_NAME \
--schedule "0 1 * * *" \
--cmd "acr purge --filter 'myapp:.*' --ago 30d --untagged" \
--context /dev/null
Run Regular Health Checks in Your Monitoring Pipeline
Add az acr check-health --name YOUR_REGISTRY_NAME --yes to your infrastructure monitoring scripts. Run it daily, at minimum. Catching a DNS resolution failure or a Docker daemon misconfiguration in your CI agents before a deployment window saves you from a very bad day.
- Enable the Admin account only for emergency break-glass access, never use it in automated pipelines. It's a single shared credential that can't be rotated per-user.
- Tag every image with both a version tag (
v1.0.3) andlatest. Never deploylatestin production, always pin to a version tag so you know exactly what's running. - If you're on Premium, enable geo-replication to the same Azure region as your AKS cluster or App Service. The latency improvement on image pulls at pod startup is measurable, and in autoscaling scenarios, it matters a lot.
- Enable Microsoft Defender for Containers integration on your registry so that every pushed image gets vulnerability scanned automatically. You get findings in the Azure portal under Microsoft Defender for Cloud > Recommendations.
Frequently Asked Questions About Azure Container Registry
What is Azure Container Registry and how is it different from Docker Hub?
Azure Container Registry is Microsoft's managed, private registry service for storing and managing container images and related artifacts like Helm charts and OCI-format content. The biggest difference from Docker Hub is privacy and integration: your images in ACR are private by default and live inside your Azure subscription, directly accessible to Azure Kubernetes Service, App Service, Azure Batch, and your CI/CD pipelines without additional configuration. Docker Hub is a public registry by default, and private repositories cost extra. ACR is also backed by Azure's SLA, sits behind your Azure network controls, and integrates natively with Microsoft Entra ID for identity-based access, you don't manage separate Docker Hub credentials.
Why do I keep getting "UNAUTHORIZED: authentication required" even after running az acr login?
This almost always means one of two things: either your ACR login token has expired (they're valid for about 3 hours), or your Azure identity doesn't have the AcrPush or AcrPull role on the registry resource. Run az acr login --name YOUR_REGISTRY_NAME to refresh the token. If you still get 401 after that, check your RBAC assignment with az role assignment list --assignee YOUR_EMAIL --scope $(az acr show --name YOUR_REGISTRY_NAME --query id --output tsv). If AcrPush or AcrPull isn't listed, add the role, and then wait 2–5 minutes for it to propagate before retrying.
Which SKU should I pick, Basic, Standard, or Premium?
Basic is for individual development and small-scale testing only. It has low storage limits and no support for geo-replication or private endpoints. Standard is the right default for team environments and production workloads that don't need multi-region distribution. Premium is the tier for enterprise and multi-region scenarios, it adds geo-replication for distributing images close to your deployment regions, private endpoints for locking the registry behind a VNet, content trust for image signature verification, and significantly higher storage and throughput limits. You can upgrade between tiers at any time without losing data, so it's fine to start lower and move up, but don't run production CI/CD on Basic and wonder why you're being throttled.
How do I fix "Error response from daemon: login attempt to https://myregistry.azurecr.io/v2/ failed" on Windows?
On Windows, this error is most often caused by Docker Desktop not being running, an outdated Azure CLI, or a Windows Credential Manager entry that's caching bad credentials. First, confirm Docker Desktop shows "Engine running." Then run az upgrade to ensure your CLI is current. If the error persists, open Windows Credential Manager (search for it in the Start menu), go to Windows Credentials, find any entries for https://yourregistry.azurecr.io, and delete them. Then run az acr login --name YOUR_REGISTRY_NAME fresh. The stale cached credential is often the culprit on Windows machines that have worked before and suddenly started failing.
Can I use Azure Container Registry with Kubernetes, including non-Azure Kubernetes clusters?
Yes, and the setup is different depending on whether you're on AKS or an external cluster. For Azure Kubernetes Service, you can attach an ACR directly to the cluster with one command: az aks update --name YOUR_AKS_CLUSTER --resource-group YOUR_RG --attach-acr YOUR_REGISTRY_NAME. This grants the cluster's managed identity AcrPull access automatically, no image pull secret needed. For non-Azure Kubernetes clusters (on-premises, GKE, EKS), you need to create a Kubernetes image pull secret containing your service principal credentials: kubectl create secret docker-registry acr-secret --docker-server=YOUR_REGISTRY_NAME.azurecr.io --docker-username=YOUR_SP_APP_ID --docker-password=YOUR_SP_PASSWORD, then reference that secret in your pod spec under imagePullSecrets.
How do I enable anonymous (unauthenticated) pull access in Azure Container Registry?
Azure Container Registry does support anonymous pull access, but you have to explicitly turn it on, it's off by default, which is the secure and correct default for a private registry. You enable it at the registry level, not per-repository. Via CLI: az acr update --name YOUR_REGISTRY_NAME --anonymous-pull-enabled true. Once enabled, anyone who knows your registry login server can pull images from it without authenticating. Think carefully before enabling this, it's appropriate for public artifact distribution scenarios, not for proprietary application images. You can disable it just as easily with --anonymous-pull-enabled false. Note that push operations always require authentication regardless of this setting.