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 eastus

The 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:latest

Option 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:

RolePermissionsWho needs it
AcrPullPull images onlyAKS node pools, ACI, App Service, Container Apps pulling images
AcrPushPush and pull imagesCI/CD pipelines building and pushing images
AcrDeleteDelete imagesCleanup automation
Owner / ContributorFull control planeRegistry 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_ID

With this setup, AKS node pools pull images from ACR using the kubelet managed identity — no secrets to rotate, no credentials to store in Kubernetes.

Tip

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:latest

Run 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/null

Vulnerability 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 Standard

Defender 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

  1. 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.
  2. Using the latest tag exclusively. Tagging every image as :latest makes it impossible to roll back to a previous version. Tag images with the build number, git commit SHA, or semantic version. Keep :latest as an alias pointing to the most recent version, not as the only tag.
  3. 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.

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.

Last verified: 19 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.