Container Registry
Introduction
Section titled “Introduction”Azure Container Registry (ACR) is a managed, private OCI-compatible registry for storing container images and Helm charts. It integrates natively with Azure Kubernetes Service, Azure Container Instances, and Azure App Service to streamline container-based application deployments. ACR is commonly used to build, store, and manage container images as part of a continuous integration and deployment pipeline. For more information, see What is Azure Container Registry?.
LocalStack for Azure provides a local environment for building and testing applications that make use of Azure Container Registry. Each registry is backed by its own local OCI registry, and its login server serves the Azure Container Registry token protocol, so Docker and the Azure CLI push, pull, and sign in as they do with Azure. The supported APIs are available on our API Coverage section, which provides information on the extent of Container Registry’s integration with LocalStack.
Getting started
Section titled “Getting started”This guide walks you through creating a registry, logging in with Docker, pushing, pulling, and importing images, and listing repositories.
Launch LocalStack using your preferred method. For more information, see Introduction to LocalStack for Azure. Once the container is running, enable Azure CLI interception by running:
lstk az start-interceptionThis command points the az CLI away from the public Azure management REST API and toward the LocalStack for Azure emulator API.
To revert this configuration, run:
lstk az stop-interceptionThis reconfigures the az CLI to send commands to the official Azure management REST API.
Create a resource group
Section titled “Create a resource group”Create a resource group to hold all resources created in this guide:
az group create --name rg-acr-demo --location westeurope{ "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-acr-demo", "location": "westeurope", "name": "rg-acr-demo", "properties": { "provisioningState": "Succeeded" }, "type": "Microsoft.Resources/resourceGroups"}Create a container registry
Section titled “Create a container registry”Create a Basic-tier Azure Container Registry (Azure also offers Standard and Premium; limits differ by tier, as described in Azure Container Registry SKU features and limits):
az acr create \ --name myacrdemo \ --resource-group rg-acr-demo \ --sku Basic \ --admin-enabled true{ "adminUserEnabled": true, "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-acr-demo/providers/Microsoft.ContainerRegistry/registries/myacrdemo", "location": "westeurope", "loginServer": "myacrdemo.azurecr.azure.localhost.localstack.cloud:4566", "name": "myacrdemo", "provisioningState": "Succeeded", "resourceGroup": "rg-acr-demo", "sku": { "name": "Basic", "tier": "Basic" }, "type": "Microsoft.ContainerRegistry/registries"...}Log in with Docker
Section titled “Log in with Docker”Authenticate the local Docker daemon to the registry using the Azure CLI:
az acr login --name myacrdemoLogin SucceededWith Azure, az acr login is the documented way to authenticate Docker to your registry using your signed-in Microsoft Entra identity (Push your first image to your Azure container registry using the Docker CLI; for other auth options see Authenticate with an Azure container registry). LocalStack serves the same token exchange, so az acr login signs Docker in with your Microsoft Entra identity whether or not the admin user is enabled.
Build and push an image
Section titled “Build and push an image”Create a minimal Dockerfile for the demo image:
cat > Dockerfile <<'EOF'FROM alpine:latestCMD ["echo", "Hello from LocalStack ACR!"]EOFCapture the registry login server returned by the emulator, then build and push the image using that address:
LOGIN_SERVER=$(az acr show --name myacrdemo --resource-group rg-acr-demo --query loginServer -o tsv)docker build -t $LOGIN_SERVER/hello:v1 .docker push $LOGIN_SERVER/hello:v1Alternatively build directly within the emulated registry:
az acr build \ --image hello:v1 \ --registry myacrdemo \ .Pull an image
Section titled “Pull an image”Pull the image back from the registry to confirm it was pushed correctly:
docker pull $LOGIN_SERVER/hello:v1List repositories
Section titled “List repositories”List all image repositories stored in the registry:
az acr repository list --name myacrdemo[ "hello"]Import an image
Section titled “Import an image”Import an image from Microsoft Container Registry without pulling it locally (see Import container images to a container registry):
az acr import \ --name myacrdemo \ --resource-group rg-acr-demo \ --source mcr.microsoft.com/hello-world:latest \ --image hello-world:latestList the tags of the imported repository:
az acr repository show-tags --name myacrdemo --repository hello-world[ "latest"]Check name availability
Section titled “Check name availability”Check registry name availability against the management API. The following example assumes you already created myacrdemo above, so the response matches a name that is not available:
az acr check-name --name myacrdemo{ "message": "The registry myacrdemo is already in use.", "nameAvailable": false, "reason": "AlreadyExists"}Update registry settings
Section titled “Update registry settings”Disable the registry admin account (az acr update is the same command Azure documents for changing registry settings; see az acr update):
az acr update --name myacrdemo --resource-group rg-acr-demo --admin-enabled false{ "adminUserEnabled": false, "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-acr-demo/providers/Microsoft.ContainerRegistry/registries/myacrdemo", "loginServer": "myacrdemo.azurecr.azure.localhost.localstack.cloud:4566", "name": "myacrdemo", "provisioningState": "Succeeded", "resourceGroup": "rg-acr-demo", "sku": { "name": "Basic", "tier": "Basic" }, "type": "Microsoft.ContainerRegistry/registries"...}Show registry usage
Section titled “Show registry usage”Show current storage usage statistics for the registry:
az acr show-usage --name myacrdemo --resource-group rg-acr-demo{ "value": [ { "currentValue": 3871673, "limit": 10737418240, "name": "Size", "unit": "Bytes" }, { "currentValue": 3871673, "limit": 43980465111040, "name": "MaximumStorageCapacity", "unit": "Bytes" }, { "currentValue": 0, "limit": 2, "name": "Webhooks", "unit": "Count" }, { "currentValue": 0, "limit": 100, "name": "ScopeMaps", "unit": "Count" }, { "currentValue": 0, "limit": 100, "name": "Tokens", "unit": "Count" } ]}The limits are those of the registry’s tier, as listed in Azure Container Registry SKU features and limits.
Delete and verify
Section titled “Delete and verify”Delete the registry and remove the demo Dockerfile created earlier:
az acr delete --name myacrdemo --resource-group rg-acr-demo --yesrm -f DockerfileDeleting a registry also deletes its images, so a registry you create again with the same name starts empty. Verify that the resource group no longer contains a registry:
az acr list --resource-group rg-acr-demo[]Features
Section titled “Features”- Registry lifecycle: Create, show, list, update, and delete registries using the Azure CLI or the ARM API. Deleting a registry deletes its images, so a registry created again under the same name starts empty.
- Token authentication: The login server implements the Azure Container Registry token protocol, so
az acr loginsigns Docker in with your Microsoft Entra identity whether or not the admin user is enabled, and theazure-containerregistrySDK signs in with a Microsoft Entra credential. Docker and containerd can also sign in with the admin user or a token, and credentials Azure refuses, such as a wrong password or the password of a disabled admin user, are refused. - Admin user management: Enable or disable the admin user, and show or regenerate its passwords. As on Azure, the passwords cannot be shown or regenerated while the admin user is disabled.
- Scope maps and tokens: A token grants only the actions of its scope map, either one you create or one of the system-defined scope maps every registry has, such as
_repositories_pull. A disabled token grants nothing. - Image push and pull: Push and pull images with the Docker CLI. Each registry is backed by a local OCI registry, so an image keeps its manifest and digest.
- Repository metadata: List and show repositories, tags, and manifests with
az acr repository list,az acr repository show,az acr repository show-tags, andaz acr manifest list-metadata, including paging and ordering. - Image import: Import images with
az acr importfrom another registry in LocalStack or from a public registry such as Docker Hub or Microsoft Container Registry. The registry stores the source’s own manifests and layers, so an imported image keeps its digest. - Builds and tasks:
az acr buildbuilds the image with your local Docker daemon and pushes it to the registry, and a failed build fails the command. Tasks created withaz acr task createrun withaz acr task run, and runs can be shown, listed, archived, and canceled. - Webhooks: Webhooks deliver ping, push, and delete events to their service URI with the payload and headers Azure sends, and
az acr webhook list-eventslists each delivery with the endpoint’s response. - Name availability check: Check whether a registry name is available with
az acr check-name. As on Azure, a registry name is unique across subscriptions and resource groups. - Registry settings: Network rules, public network access, the data endpoint, policies, encryption, identities, and tags are stored and returned as Azure returns them. A system-assigned identity is a real principal that you can assign roles to.
- Service tiers: Basic, Standard, and Premium registries report their tier’s limits with
az acr show-usage. As on Azure, a registry below Premium refuses network rules that restrict access, the data endpoint, and an enabled retention policy, and a Basic registry refuses anonymous pull. A move to a lower tier is refused while the registry keeps what that tier cannot have, such as IP rules or an enabled retention policy on a move from Premium, or anonymous pull on a move to Basic.
Limitations
Section titled “Limitations”- RBAC enforcement is opt-in: By default, data-plane operations succeed regardless of role assignments. Set
LS_AZURE_ENFORCE_RBACto require a Microsoft Entra caller to hold a role such asAcrPull,AcrPush, orAcrDelete, or a Container Registry Repository role on a registry that uses ABAC; the admin user and tokens keep the access their own credentials grant. The conditions of ABAC role assignments are not evaluated, so a role assigned for one repository applies to the whole registry. See Role Assignment: Enabling RBAC enforcement. - Requests without credentials are allowed: A pull or push without credentials is treated as coming from LocalStack’s default principal, which holds every permission, so Container Apps, Container Instances, and App Service can pull images without credentials. Azure refuses such a request unless the registry allows anonymous pull, and then only for pulls. Under RBAC enforcement, a pull from an AKS node counts as the cluster’s kubelet identity, as on Azure.
- Access and refresh tokens are not verified: LocalStack does not check the signature or expiry of the access and refresh tokens its login server issues, and the Microsoft Entra token that
az acr loginexchanges only needs claims that can be read. - Network settings are not enforced: Network rules, public network access, and private endpoint connections are stored and returned but do not restrict access to the registry. The data endpoint host names are returned but not served, since the login server serves all data.
- Customer-managed keys are not applied: A customer-managed encryption key is stored and returned but not applied, and it is accepted on any tier, whereas Azure requires Premium.
- Tier limits are not enforced: The webhook, token, and scope map counts that
az acr show-usagereports are not enforced, and a private endpoint to a Basic or Standard registry is connected, whereas Azure requires Premium. - Registry policies are not enforced: Quarantine, retention, and export policies are stored and returned but have no effect: new images are never quarantined, and untagged manifests are never purged.
- Preview registry properties: Properties that the Azure CLI reads from a preview API version, such as metadata search, regional endpoints, the endpoint protocol, and the domain name label scope, are not supported, so
az acrcommands show them asnull. The soft-delete policy shows asnulluntil a request sets one, and then has no effect. - Geo-replication: Replications can be created on a Premium registry, but each registry has a single local backend, so images are not replicated.
- Task steps are not run: Task files and encoded tasks (
az acr run -f,az acr run --cmd, and tasks defined with them) are validated and reported successful without running their steps. Task triggers (source commits, base image updates, and timers) are stored but never start a run, and a task’s source must be uploaded withaz acr task run --context <local folder>, since LocalStack does not fetch sources such as Git repositories. - Webhook targets: A webhook can call any host, including one on your machine (
host.docker.internalfrom inside the LocalStack container), whereas Azure requires a public address. Helm chart and quarantine events are never delivered. - Repository changes: Changing the attributes of repositories, tags, and manifests, and deleting a repository or a tag, are not supported yet, so
az acr repository update,az acr repository untag, andaz acr repository delete --repositoryfail. Deleting an image withaz acr repository delete --imageworks.
Samples
Section titled “Samples”The following samples demonstrate how to use Azure Container Registry with LocalStack for Azure:
- Azure Container Instances, Key Vault, and Storage (Python)
- Guestbook on Azure Container Apps with Blob Storage and Container Registry (Python)
- Web App with a Custom Docker Image (Python)
- Web App with a Custom Docker Image (.NET)
Every sample in the AKS samples also pushes its image to a registry with az acr login and runs it on AKS.
API Coverage
Section titled “API Coverage”79 of 79 operations implemented
| Operation ▲ | Implemented ▼ |
|---|