Azure Container Registry Overview: Store and Manage Container Images
Azure Container Registry (ACR) is Azure’s private Docker-compatible registry for storing container images, Helm charts, and OCI artifacts. It integrates directly with AKS, App Service, Container Apps, and ACI — letting those services pull images using managed identities rather than stored credentials. This page covers creating a registry, pushing and pulling images, and setting up access control correctly.
Why a private registry matters
Docker Hub is the default public registry, but using it for private application images creates several problems:
- Access control. Docker Hub’s private repository model requires managing Docker Hub credentials separately from Azure RBAC. ACR uses Azure RBAC, so access is controlled through the same role assignments you use for everything else in Azure.
- Network performance. Pulling images from Docker Hub introduces internet latency and potential rate limits. ACR deployed in the same region as your compute resources is much faster and not rate-limited.
- Security scanning. ACR integrates with Microsoft Defender for Containers to scan images for known vulnerabilities on push and periodically thereafter.
- Geo-replication. Premium ACR can replicate images to multiple regions, so AKS clusters in East US and West Europe both pull from a nearby registry replica rather than crossing regions.
Creating a registry
# Create a resource group
az group create --name my-rg --location eastus
# Create a Standard tier ACR
az acr create \
--resource-group my-rg \
--name myorgacr \
--sku Standard \
--location eastusThe registry name must be globally unique — it becomes part of the FQDN: myorgacr.azurecr.io. Use lowercase letters and numbers only (no hyphens). Standard tier is appropriate for most production workloads.
Building and pushing images
Option 1: Build locally, push to ACR
# Log in to the registry using your Azure CLI session
az acr login --name myorgacr
# Build your image locally
docker build -t myapp:latest .
# Tag the image with the ACR login server
docker tag myapp:latest myorgacr.azurecr.io/myapp:latest
# Push to ACR
docker push myorgacr.azurecr.io/myapp:latestOption 2: Build in ACR (no local Docker needed)
# ACR Tasks builds the image in Azure — no local Docker required
az acr build \
--registry myorgacr \
--image myapp:latest \
.az acr build uploads your build context to Azure, builds the Docker image using ACR’s build infrastructure, and pushes the result to the registry. This is useful in CI/CD pipelines or on developer machines where Docker is not installed. Build logs stream to the terminal so you can monitor progress.
Access control: RBAC over admin credentials
ACR supports two access methods:
Admin account (avoid for production)
Every ACR has an optional admin account with a username/password. It is disabled by default. Do not enable it for production — the credentials do not rotate automatically, there is no audit trail of who used them, and sharing them across teams means you cannot revoke individual access.
RBAC with managed identities (recommended)
Assign the appropriate built-in role to the managed identity (or service principal) that needs registry access:
| Role | Permissions | Who needs it |
|---|---|---|
| AcrPull | Pull images only | AKS node pools, ACI, App Service, Container Apps pulling images |
| AcrPush | Push and pull images | CI/CD pipelines building and pushing images |
| AcrDelete | Delete images | Cleanup automation |
| Owner / Contributor | Full control plane | Registry administrators — not for image operations |
# Grant AKS the AcrPull role (so node pools can pull images without credentials)
ACR_ID=$(az acr show --name myorgacr --resource-group my-rg --query id --output tsv)
AKS_IDENTITY=$(az aks show --name my-aks --resource-group my-rg \
--query identityProfile.kubeletidentity.objectId --output tsv)
az role assignment create \
--assignee $AKS_IDENTITY \
--role AcrPull \
--scope $ACR_IDWith this setup, AKS node pools pull images from ACR using the kubelet managed identity — no secrets to rotate, no credentials to store in Kubernetes.
When creating an AKS cluster, the —attach-acr flag automates this entire process: az aks create —attach-acr myorgacr …. It creates the AcrPull role assignment for the cluster’s managed identity in one step.
Managing images and storage costs
Each push to ACR creates a new image manifest. Images accumulate quickly in active CI/CD pipelines — a team pushing 10 builds per day creates 300 image versions per month. Without a cleanup policy, storage costs grow unchecked.
List images and their sizes:
# List all repositories in the registry
az acr repository list --name myorgacr --output table
# List tags for a specific image
az acr repository show-tags \
--name myorgacr \
--repository myapp \
--orderby time_desc \
--output table
# Show size of a specific image
az acr repository show \
--name myorgacr \
--image myapp:latestRun a purge to clean up old untagged images (images pushed without a specific tag or whose tag was overwritten):
# Delete untagged manifests older than 7 days (dry run first)
az acr run \
--registry myorgacr \
--cmd "acr purge --filter 'myapp:.*' --untagged --ago 7d --dry-run" \
/dev/null
# Run without --dry-run to delete
az acr run \
--registry myorgacr \
--cmd "acr purge --filter 'myapp:.*' --untagged --ago 7d" \
/dev/nullVulnerability scanning
Microsoft Defender for Containers scans images in ACR for known CVEs (Common Vulnerabilities and Exposures). Scanning happens automatically on push and on a schedule. Findings appear in Microsoft Defender for Cloud.
Enable Defender for Containers on the subscription to activate scanning:
az security pricing create \
--name Containers \
--tier StandardDefender for Cloud shows each image with a list of CVEs, their severity, and whether a fix is available. For CI/CD pipelines, integrate this feedback loop: fail the pipeline if a newly pushed image contains critical vulnerabilities with available fixes. This catches known vulnerabilities before they reach production.
Common ACR mistakes
- Enabling the admin account for production access. The admin account has a static username and password. Use managed identities and RBAC instead. If the admin password leaks, you cannot audit who used it or selectively revoke individual access.
- Using the latest tag exclusively. Tagging every image as
:latestmakes it impossible to roll back to a previous version. Tag images with the build number, git commit SHA, or semantic version. Keep:latestas an alias pointing to the most recent version, not as the only tag. - No image cleanup policy. Every CI/CD build that pushes an image without removing old ones increases your ACR storage bill. Set up a scheduled purge task or configure a retention policy on the registry from the beginning, not after storage costs spike.
Summary
- ACR is Azure’s private container registry, integrating with RBAC so managed identities can pull images without credentials.
- Use
az acr buildto build images in the cloud without requiring Docker locally — useful for CI/CD pipelines. - Grant AcrPull to compute identities (AKS, ACI, App Service), AcrPush to CI/CD pipelines. Never enable the admin account for production.
- Tag images with meaningful identifiers (commit SHA, version number). Avoid relying on
:latestalone. - Set up image cleanup policies from day one to prevent unchecked storage growth from CI/CD builds.
Frequently asked questions
What is the difference between Basic, Standard, and Premium ACR tiers?
Basic is sufficient for individual developers and small teams: 10 GB storage, limited webhooks, no geo-replication. Standard adds 100 GB storage and more webhook support. Premium adds geo-replication (replicate images to multiple regions for low-latency pulls), content trust (image signing), private link support, and customer-managed keys. For most teams, Standard covers production needs; Premium is for multi-region deployments or strict compliance requirements.
Can I use Azure Container Registry with GitHub Actions?
Yes. Use the azure/docker-login action or az acr login with a service principal or OpenID Connect (OIDC) federated identity. OIDC is the recommended approach for GitHub Actions — it does not require storing a long-lived secret, instead exchanging a short-lived token for registry credentials. See the Azure/login GitHub Action documentation for setup.
How do I delete old images to control storage costs?
Use ACR lifecycle policies (retention policies) to automatically purge untagged manifests and tags older than a specified number of days. You can also run az acr purge manually. ACR storage is billed by GB, so old images from many builds accumulate cost quickly without a cleanup policy.