Home / Azure

AzureHow-toIntermediate

Transformation: containerise VM-hosted services and run them on Azure Container Apps

Turn APIs and workers that run on VMs into containers on Azure Container Apps. Write the Dockerfile, push to ACR, deploy with managed identity and secrets, set scale rules, and plan the traffic switch.

Deepak Bhardwaj3 min read

Applies toAzure Container Apps (workload profiles environment), Azure Container Registry, Docker, Azure CLI 2.50+

Why Container Apps

Container Apps runs containers without you managing Kubernetes: revisions, HTTPS ingress, KEDA-based autoscaling (including scale to zero), Dapr and jobs are built in. It fits stateless APIs, background workers and scheduled jobs. Choose AKS instead when you need full Kubernetes APIs, custom operators or node-level control.

1. Pick the right first service

Good first candidates are stateless, have a health endpoint, read config from environment variables and log to stdout. Avoid services that write to local disk, need Windows-only components or hold sticky sessions in memory.

2. Containerise

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 8080
ENV ASPNETCORE_URLS=http://+:8080

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish Orders.Api.csproj -c Release -o /out

FROM base
COPY --from=build /out .
USER app
ENTRYPOINT ["dotnet", "Orders.Api.dll"]

Run as non-root, keep images small, and pin base image versions.

3. Build and push

az acr create -g rg-apps -n acrprodapps --sku Premium
az acr build -r acrprodapps -t orders-api:1.0.0 .

4. Environment and app

az containerapp env create -g rg-apps -n cae-prod -l westeurope \
  --infrastructure-subnet-resource-id <subnet-id> --internal-only true

az containerapp create -g rg-apps -n orders-api --environment cae-prod \
  --image acrprodapps.azurecr.io/orders-api:1.0.0 \
  --registry-server acrprodapps.azurecr.io --registry-identity system \
  --ingress internal --target-port 8080 \
  --min-replicas 2 --max-replicas 10 \
  --cpu 0.5 --memory 1Gi

Secrets: use Key Vault references with the app's managed identity rather than plain secrets.

az containerapp identity assign -g rg-apps -n orders-api --system-assigned
az containerapp secret set -g rg-apps -n orders-api \
  --secrets "sql-conn=keyvaultref:https://kv-apps.vault.azure.net/secrets/sql-conn,identityref:system"
az containerapp update -g rg-apps -n orders-api --set-env-vars "ConnectionStrings__Main=secretref:sql-conn"

5. Scale rules

az containerapp update -g rg-apps -n orders-api \
  --scale-rule-name http-rule --scale-rule-type http --scale-rule-http-concurrency 50

Workers can scale on queue length (Service Bus, Storage queue) with KEDA scale rules.

6. Health probes and revisions

Configure liveness and readiness probes on your health endpoint. Use multiple revision mode to send a percentage of traffic to a new revision and roll back by shifting weight.

7. Switch traffic from the VMs

Put Application Gateway or Front Door in front of both the VM pool and the Container App. Shift traffic in steps (10%, 50%, 100%) while watching error rate and latency, then retire the VMs.

Warning

Scale to zero gives cold starts. For user-facing APIs keep min-replicas at 2 or more.

Checklist

  • Service is stateless and configured by environment variables
  • Image built in CI, scanned, pushed to ACR with immutable tags
  • Managed identity for ACR pull and Key Vault
  • Probes, scale rules and min replicas set
  • Gradual traffic shift with a tested rollback