Store and Manage Containers in Azure Container Registry
Introduction
2 minAzure Container Registry (ACR) is Azure's private Docker-compatible registry. It stores container images and OCI artifacts, integrates natively with AKS, Container Apps, and App Service, and runs ACR Tasks to build images without a local Docker engine. Think: Private Docker Hub + Azure RBAC + built-in vulnerability scanning.
Registries, Repositories, and Artifacts
8 minACR β Azure Compute: Build once, deploy everywhere
1. Registry Hierarchy
- Registry β top-level ACR account with unique DNS:
myapp.azurecr.io. Up to 500 repositories per registry. - Repository β named collection of related images (for example:
api,worker,batch). - Tag β mutable pointer to an image digest (for example:
latest,v2.1). Can be overwritten. - Digest β immutable SHA-256 hash (
sha256:abc123...). Survives tag rewrites. Always use in production.
Memory aid: Library β Bookshelf β Label β ISBN
2. ACR SKUs Compared
| # | SKU | Storage | Geo-Replication | Private Link | Use For |
|---|---|---|---|---|---|
| 1 | Basic | 10 GB | β | β | Dev/test, learning |
| 2 | Standard | 100 GB | β | β | Most production workloads |
| 3 | Premium | 500 GB | β | β | Multi-region, private networks, HSM keys |
3. ACR Authentication Options
- Microsoft Entra ID + RBAC (recommended) β roles:
AcrPull(read-only),AcrPush(push),AcrDelete(delete). Works with managed identities β no stored credentials. - Admin account β single username + 2 rotatable passwords. Disabled by default. Only for legacy tools that can't use Entra. Never production.
- Repository-scoped tokens β Premium only. Grants access to specific repos only. Great for vendors/external CI.
β‘ ACR Auth Quick Reference
Build Container Images with ACR Tasks
8 minACR Tasks β Three Types
- Quick Task β one-shot cloud build. No local Docker daemon needed. Ideal for CI pipelines.
az acr build --registry myregistry --image api:v1 . - Triggered Task β runs automatically on an event:
- Source trigger β on git commit or PR via GitHub/ADO webhook
- Base image trigger β rebuilds your image when its
FROMbase image is updated (Premium only) - Schedule trigger β cron-style, runs on a timer for nightly scans
az acr task create \ --registry myregistry --name build-on-commit \ --image "api:{{.Run.ID}}" \ --context https://github.com/org/repo.git --branch main \ --file Dockerfile --git-access-token $TOKEN - Multi-Step Task (YAML) β build β test β push pipeline in YAML:
version: v1.1.0 steps: - build: -t myregistry.azurecr.io/api:test -f Dockerfile . - push: ["myregistry.azurecr.io/api:test"] - cmd: myregistry.azurecr.io/api:test --run-tests - build: -t myregistry.azurecr.io/api:latest -f Dockerfile . - push: ["myregistry.azurecr.io/api:latest"]
Tag, Version, and Lifecycle Management
8 min1. Stable vs Unique Tags
- Stable tags (for example:
latest,v1) β mutable. Convenient but silently change. Dev/test only. - Unique tags (for example:
v1.2.3-20250601) β immutable per build. Fully traceable. Use in production. - Digests (
image@sha256:abc...) β never changes even if tag is overwritten. Gold standard for prod.
2. Locking Images (Write-Protection)
az acr repository update \
--name myregistry --image api:v1.2.3 --write-enabled false Locked images cannot be overwritten or deleted. Protects production images from accidental pushes.
3. Lifecycle Policies (Auto-Cleanup)
az acr config retention update \
--registry myregistry --status enabled --days 30 --type UntaggedManifests Auto-deletes untagged manifests older than N days. Prevents storage bloat from accumulated dangling layers.
π§ Memory Tricks
Auth ladder: Managed Identity (always first) > Repo token (vendors) > Admin (legacy only, never prod)
SKU β features: B/S = basic storage, P = Premium = Private link + geo-replication
Task types: QTS β Quick, Triggered, Step
Exercise β Build and Push with ACR Tasks
30 min- Create an ACR registry (Standard SKU)
- Run
az acr buildto build without local Docker - Create a triggered task tied to a GitHub repo
- Verify images:
az acr repository listandaz acr repository show-tags - Lock the production tag with
--write-enabled false
Knowledge Check
5 min- Q: Which SKU supports geo-replication? A: Premium
- Q: AKS needs to pull from ACR securely. Best approach? A: Assign AcrPull role to AKS cluster's managed identity
- Q: What ACR Task type rebuilds your image when the base OS image is patched? A: Base image trigger (Triggered task β Premium only)
- Q: How do you prevent a production image tag from being overwritten? A:
az acr repository update --write-enabled false - Q: What does the ACR admin account provide? A: Username + 2 rotatable passwords β legacy auth only, never production
Summary
2 minACR provides a private, Azure-native container registry. Use Premium for geo-replication and private endpoints. Authenticate with managed identity + AcrPull β never admin in production. Use ACR Tasks (QTS) to build in the cloud. Use unique tags + digests for production traceability. Set lifecycle policies to auto-clean dangling manifests.
Deploy Containers to Azure App Service
Introduction
3 minAzure App Service can run your custom Docker containers alongside its built-in language runtimes. You push an image to ACR, point App Service at it, and Azure handles TLS, scaling, and restarts. This module covers deploying, configuring runtime settings, handling secrets, and troubleshooting container failures.
Deploy Containers to App Service
8 min1. Image Sources
- Azure Container Registry β recommended. Native integration with managed identity. Best for production.
- Docker Hub β public images or authenticated private repos. Avoid for production (rate limits, no RBAC).
- Private registry β any registry exposing the Docker HTTP API. Supply server URL + credentials.
2. ACR Authentication for App Service
- Managed Identity (recommended)
az webapp identity assign -g rg -n myapp az role assignment create \ --assignee $(az webapp identity show -g rg -n myapp --query principalId -o tsv) \ --role AcrPull \ --scope $(az acr show -n myregistry --query id -o tsv) az webapp config container set -n myapp -g rg \ --docker-custom-image-name myregistry.azurecr.io/api:v1 \ --docker-registry-server-url https://myregistry.azurecr.io - Admin credentials (legacy) β enable admin on ACR, set server/user/password in App Service config. Stores long-lived credentials.
--docker-registry-server-user or --docker-registry-server-password. The identity handles it. This distinction is tested. 3. Continuous Deployment (CD)
az webapp deployment container config -n myapp -g rg --enable-cd true Creates a webhook URL. Configure ACR to POST to it on image push. App Service auto-pulls the new image β zero CI/CD pipeline needed for simple scenarios.
Configure Container Runtime Behavior
7 min1. Port Mapping β WEBSITES_PORT
App Service routes all traffic to the port specified by WEBSITES_PORT. Default assumption is port 80.
az webapp config appsettings set -g rg -n myapp --settings WEBSITES_PORT=8000 2. Custom Startup Command
az webapp config set -g rg -n myapp --startup-file "gunicorn app:app --bind 0.0.0.0:8000" Overrides the image CMD/ENTRYPOINT at runtime without rebuilding. Useful for multi-environment configs.
3. Persistent Storage
By default the container filesystem is ephemeral β lost on restart. Set WEBSITES_ENABLE_APP_SERVICE_STORAGE=true to mount /home persistently for models, logs, or write-able files.
4. Health Checks
- Always On β keeps app warm (no cold start). Enable for production, disable to save cost on dev.
- Health check path β App Service polls your endpoint (for example:
/health). Unhealthy instances are recycled.
Configure Application Settings
6 min1. App Settings β Environment Variables
az webapp config appsettings set -g rg -n myapp --settings MODEL_NAME=gpt-4o LOG_LEVEL=info Every App Setting becomes an environment variable in your container β the primary config injection mechanism.
2. Key Vault References (Secure Secrets)
az webapp config appsettings set -g rg -n myapp \
--settings "[email protected](SecretUri=https://myvault.vault.azure.net/secrets/api-key/)" App Service fetches the secret at runtime. Secret rotations propagate automatically β no redeployment needed.
3. Connection String Prefixes
SQLAZURECONNSTR_β SQL AzureMYSQLCONNSTR_β MySQLPOSTGRESQLCONNSTR_β PostgreSQLCUSTOMCONNSTR_β any custom connection string
Observe and Troubleshoot Containers
7 min1. Log Stream (Live Logs)
az webapp log tail -g rg -n myapp Streams container stdout/stderr in real time. First stop when a deployment fails.
2. SSH Console (Kudu)
App Service exposes SSH to running containers via https://myapp.scm.azurewebsites.net. Exec into the container and diagnose issues interactively. Your container must expose port 2222 for SSH to work.
3. Application Insights
Set APPLICATIONINSIGHTS_CONNECTION_STRING as an App Setting + add the SDK. Provides request tracing, exceptions, and performance metrics.
β‘ ACR + App Service Master Cheatsheet
az acr build --registry ... --image ... .WEBSITES_PORT=8000--enable-cd true--write-enabled falseaz acr config retention update --days 30az webapp log tail -g rg -n myappExercise β Deploy a Container to App Service
30 min- Build an image with
az acr build - Create a Linux App Service (Docker container plan)
- Configure managed identity + AcrPull for registry auth
- Set
WEBSITES_PORTand env vars - Add a Key Vault reference for the API key
- Verify with
az webapp log tail
Knowledge Check
5 min- Q: App Service fails to start your container β first thing to check? A: WEBSITES_PORT matches your app's listen port
- Q: How does App Service pull from ACR without stored credentials? A: System-assigned managed identity + AcrPull RBAC role on the registry
- Q: Where do you view live stdout from a running container? A:
az webapp log tailor Log Stream in Azure portal - Q: A Key Vault reference shows as literal text in the app, not the secret value. Why? A: The managed identity is missing the Key Vault Secrets User role
Summary
2 minDeploy containers to App Service using ACR with managed identity authentication. Set WEBSITES_PORT to match your app's listen port. Inject configuration via App Settings and reference secrets from Key Vault instead of embedding them. Use log streaming and SSH console to troubleshoot. Enable continuous deployment for auto-pull on new image pushes.
π§ Container Startup Failure Checklist
- Check
WEBSITES_PORTmatches app listen port - Check image tag is correct and registry credentials work
- Run
az webapp log tailfor app-level errors - Check managed identity has AcrPull on the registry
- Check Key Vault references if secrets are missing
Azure Container Registry + App Service
π Key Facts
- az acr build β Cloud build β no local Docker. Runs in Azure.
- AcrPull role β Managed Identity pulls images. NEVER admin user in prod.
- SKU ladder β Basic (10 GB dev) β Standard (100 GB prod) β Premium (geo-rep)
- Geo-replication β Premium ONLY. Push once, pull from nearest replica.
- Unique tag β Immutable per build β use in production (not 'latest')
- WEBSITES_PORT β Must match app listen port β #1 startup failure cause
- KV Reference β @Microsoft.KeyVault(SecretUri=...) in App Settings
- Base image trigger β Auto-rebuild on OS update β Premium ACR task only
π» Commands & Patterns
az acr build --registry myacr --image api:v1 . az acr task create --name build --registry myacr --image "api:{{{.Run.ID}}}" --context https://github.com/org/repo --branch main --git-access-token $TOKEN az acr config retention update --registry myacr --status enabled --days 30 az acr repository update --name myacr --image api:v1.2.3 --write-enabled false az webapp config appsettings set -g rg -n app --settings WEBSITES_PORT=8000 az webapp log tail -g rg -n myapp