Azure Container Apps — Serverless Containers on Microsoft Azure
Advertisement
Introduction
Why This Matters
Azure Container Apps provides managed Kubernetes-based container hosting without exposing cluster complexity. Built on KEDA (Kubernetes Event-Driven Autoscaling) and Dapr (Distributed Application Runtime), it handles HTTP scaling, queue-driven scaling, service discovery, and distributed tracing out of the box. It sits between Azure App Service (simple PaaS) and AKS (full Kubernetes) in the complexity spectrum — ideal for microservices teams that need scale without ops burden.
Installation and Setup
# Install Azure CLI and Container Apps extension
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
az extension add --name containerapp --upgrade
# Login and set subscription
az login
az account set --subscription my-subscription-id
# Register required providers
az provider register --namespace Microsoft.App
az provider register --namespace Microsoft.OperationalInsightsCreating Environments and Apps
# Create resource group
az group create \
--name myapp-rg \
--location eastus
# Create Log Analytics workspace
az monitor log-analytics workspace create \
--resource-group myapp-rg \
--workspace-name myapp-logs
# Get workspace credentials
WORKSPACE_ID=$(az monitor log-analytics workspace show \
--resource-group myapp-rg \
--workspace-name myapp-logs \
--query customerId --output tsv)
WORKSPACE_KEY=$(az monitor log-analytics workspace get-shared-keys \
--resource-group myapp-rg \
--workspace-name myapp-logs \
--query primarySharedKey --output tsv)
# Create Container Apps environment
az containerapp env create \
--name myapp-env \
--resource-group myapp-rg \
--location eastus \
--logs-workspace-id $WORKSPACE_ID \
--logs-workspace-key $WORKSPACE_KEY
# Deploy container app
az containerapp create \
--name my-api \
--resource-group myapp-rg \
--environment myapp-env \
--image myregistry.azurecr.io/my-api:v1.0 \
--target-port 8080 \
--ingress external \
--min-replicas 1 \
--max-replicas 20 \
--cpu 0.5 \
--memory 1Gi \
--env-vars NODE_ENV=production LOG_LEVEL=info \
--query properties.configuration.ingress.fqdn
# Update app with new image (creates new revision)
az containerapp update \
--name my-api \
--resource-group myapp-rg \
--image myregistry.azurecr.io/my-api:v1.1Revisions and Traffic Splitting
# List revisions
az containerapp revision list \
--name my-api \
--resource-group myapp-rg \
--output table
# Enable multiple active revisions
az containerapp revision set-mode \
--name my-api \
--resource-group myapp-rg \
--mode multiple
# Split traffic: 90% stable, 10% canary
az containerapp ingress traffic set \
--name my-api \
--resource-group myapp-rg \
--revision-weight \
my-api--v1=90 \
my-api--v1-1=10
# Rollback: send all traffic to stable revision
az containerapp ingress traffic set \
--name my-api \
--resource-group myapp-rg \
--revision-weight my-api--v1=100Secrets Management
# Add secrets to container app
az containerapp secret set \
--name my-api \
--resource-group myapp-rg \
--secrets db-password=MySecretPassword api-key=my-api-key-value
# Reference secrets as environment variables
az containerapp update \
--name my-api \
--resource-group myapp-rg \
--set-env-vars \
DB_PASSWORD=secretref:db-password \
API_KEY=secretref:api-key
# Use Azure Key Vault references (recommended for production)
az containerapp secret set \
--name my-api \
--resource-group myapp-rg \
--secrets kv-db-password=keyvaultref:https://mykeyvault.vault.azure.net/secrets/db-password,identityref:/subscriptions/.../managedIdentities/myapp-identityKEDA Autoscaling
# Scale on HTTP requests (built-in)
az containerapp update \
--name my-api \
--resource-group myapp-rg \
--min-replicas 0 \
--max-replicas 30 \
--scale-rule-name http-rule \
--scale-rule-type http \
--scale-rule-metadata concurrentRequests=50
# Scale on Azure Service Bus queue depth
az containerapp update \
--name queue-processor \
--resource-group myapp-rg \
--min-replicas 0 \
--max-replicas 10 \
--scale-rule-name queue-rule \
--scale-rule-type azure-servicebus \
--scale-rule-metadata \
queueName=orders \
messageCount=5 \
--scale-rule-auth \
connection=connection-string-secret:servicebus-connDapr Integration
# Enable Dapr sidecar on container app
az containerapp dapr enable \
--name my-service \
--resource-group myapp-rg \
--dapr-app-id my-service \
--dapr-app-port 8080 \
--dapr-app-protocol http
# Service-to-service calls using Dapr (automatic service discovery)
# From my-service, call another-service:
# curl http://localhost:3500/v1.0/invoke/another-service/method/endpoint
# Dapr state store component
cat > statestore.yaml << 'EOF'
componentType: state.azure.blobstorage
version: v1
metadata:
- name: accountName
value: mystorageaccount
- name: accountKey
secretRef: storage-key
- name: containerName
value: state
EOF
az containerapp env dapr-component set \
--name myapp-env \
--resource-group myapp-rg \
--dapr-component-name statestore \
--yaml statestore.yamlPrivate Registry Integration
# Link Azure Container Registry
az containerapp registry set \
--name my-api \
--resource-group myapp-rg \
--server myregistry.azurecr.io \
--identity system
# Grant AcrPull permission to the managed identity
ACR_ID=$(az acr show --name myregistry --query id --output tsv)
APP_IDENTITY=$(az containerapp show \
--name my-api \
--resource-group myapp-rg \
--query identity.principalId --output tsv)
az role assignment create \
--assignee $APP_IDENTITY \
--role AcrPull \
--scope $ACR_IDCommon Mistakes
- Not setting
--min-replicas 0to save costs on non-production apps — scale-to-zero is the main cost benefit - Using the same Container Apps environment for production and dev — environments share networking and resources
- Not enabling managed identity for registry authentication — avoid username/password registry credentials
- Setting revision mode to
singlewhen doing canary deployments — you needmultiplerevision mode for traffic splitting - Ignoring replica health checks — without proper health probes, Container Apps may route traffic to unhealthy replicas
Best Practices
- Use separate environments per stage (dev, staging, production) for network isolation
- Enable system-assigned or user-assigned managed identities for accessing Key Vault and Container Registry without secrets
- Set
--min-replicas 1for latency-sensitive production services to avoid cold starts - Use KEDA scaling rules tuned to your workload — HTTP concurrency for APIs, queue depth for workers
- Monitor with Azure Monitor and configure alerts on replica count, CPU, memory, and request failure rates
- Use Azure Container Registry with geo-replication if deploying to multiple regions
Key Takeaways
- Azure Container Apps is built on Kubernetes, KEDA, and Dapr — managed abstractions hide cluster complexity
- Environments are the networking boundary — apps in the same environment can communicate over a private VNET
- Revisions are immutable snapshots — each container update creates a new revision enabling traffic splitting and rollback
- KEDA provides event-driven autoscaling from zero — HTTP, queue depth, CPU, memory, and 50+ other triggers
- Dapr integration provides service discovery, state management, pub/sub, and observability as sidecar containers
- Secrets can reference Azure Key Vault directly — the preferred approach for production secret management
- Scale-to-zero (
--min-replicas 0) means you pay nothing when the app receives no traffic - Managed identity eliminates credential management for ACR, Key Vault, and Azure Storage access
Advertisement
Related reading
Kubernetes Guide 2026 — Deploy, Scale, and Manage Containers in Production5 min readAWS for Developers 2026 — EC2, S3, Lambda, RDS, and CloudFront Guide6 min readTerraform Infrastructure Guide 2026 — Infrastructure as Code for AWS, GCP, and Azure6 min readVercel Deployment Guide 2026 — Next.js, Edge Functions, and Production Optimization6 min readServerless Computing Guide 2026 — AWS Lambda, Cloudflare Workers, and Edge Functions7 min readDocker Best Practices in 2026 — Production-Ready Containers6 min read