A logistics company in Texas asked me to help with their order API. It ran on three virtual machines that sat mostly idle overnight and fell over every holiday sale. A developer had also pasted the database password into a Docker environment file, and that file lived in a shared folder. They wanted containers, but nobody on the team wanted to run a Kubernetes cluster.
That is the gap Azure Container Apps fills. It runs your containers on a managed, serverless platform. It scales them up and down for you and keeps you out of cluster administration. You still own the decisions that matter: identity, networking, scaling rules, secrets, and cost.
I will walk through the same pattern I used for that client. You will create an environment, deploy a container with a managed identity, pull secrets from Key Vault, add autoscaling, and monitor the result. You will also see a Bicep template and the errors you are most likely to hit.
Azure Container Apps: What It Is and When to Use It
Azure Container Apps is a serverless container platform built on Kubernetes technologies. A container is a package that holds your code and everything it needs to run. The platform handles the cluster, patching, and load balancing. It also supports scale to zero, which means no running replicas, and no compute charge, when no traffic arrives.
Four terms matter early:
- Environment: A secure boundary around one or more container apps. Apps in one environment share a virtual network and a log destination.
- Container app: Your running service, such as an API or a background worker.
- Revision: An immutable snapshot of one version of your app. New code or settings create a new revision.
- Replica: One running copy of a revision.
Choose the platform by workload. I compare the main options in this table. For a deeper look at containers on Azure, read about Web App for Containers vs AKS vs Container Instances.
| Platform | Best for | You manage | Scale to zero |
|---|---|---|---|
| Azure Container Apps | APIs, microservices, workers, event-driven jobs | App and scale rules | Yes |
| Azure App Service | Standard web apps and APIs | App and plan size | No |
| Azure Kubernetes Service | Large platforms needing full Kubernetes control | Cluster, nodes, upgrades | Limited |
| Azure Container Instances | Short, simple container runs | Container only | Not for services |
The trade-off is control. Container Apps hides the Kubernetes API, so you cannot install arbitrary cluster add-ons. If your team needs custom operators or deep node tuning, AKS fits better. If you want to compare Kubernetes offerings across clouds, see AKS vs EKS.
Pro Tip: In my experience, teams pick AKS because it sounds more “enterprise,” then spend months on upgrades. I start with Container Apps and move to AKS only when a real requirement forces it.
Plan Your Azure Architecture First
Good Azure container deployment starts with structure, not commands.
A subscription is the billing and governance boundary. I give production its own subscription whenever the company can support it, because quotas and permissions stay separate. A resource group is a logical container for related resources.
Put the environment, registry, and monitoring workspace for one application in one group, and follow the Azure resource group best practices. If you are new to groups, here is how to create a resource group in Azure.
A region is a geographic location where Azure services run. Pick one near your users and your data, and confirm Container Apps is available there.
If you want availability zones, which are separate datacenter locations inside a region, check that the region supports them. Zone redundancy needs a virtual network and a supported region, and it adds cost because Azure runs more replicas.
Decide the environment type up front:
- Consumption-only: You pay per second for the CPU and memory your replicas use. It is the simplest and cheapest for variable traffic.
- Workload profiles: You can add dedicated compute for steady or heavy workloads. You pay for reserved capacity while it runs.Pro Tip: I use separate environments for dev, test, and production. A shared environment means one bad network change breaks every stage, and cost reviews turn into guesswork.
Prepare the Environment With Azure CLI
First, sign in. This guide on the Azure CLI login covers common problems.
az extension add --name containerapp --upgrade
az provider register --namespace Microsoft.App
az provider register --namespace Microsoft.OperationalInsightsThe first command installs the Container Apps extension for the CLI. The next two register resource providers, which tell your subscription it may create these services. You need permission to register providers, and it can take a few minutes.
az group create \
--name rg-orders-dev \
--location eastusThis creates a resource group named rg-orders-dev in the East US region, giving all related resources one management boundary.
Next, create a central Log Analytics workspace, which is the data store behind Azure Monitor logs.
az monitor log-analytics workspace create \
--resource-group rg-orders-dev \
--workspace-name log-orders-dev-eastus \
--location eastusThis creates the workspace where console and system logs will land. Now create the environment and attach the workspace:
LAW_ID=$(az monitor log-analytics workspace show \
--resource-group rg-orders-dev \
--workspace-name log-orders-dev-eastus \
--query customerId --output tsv)
LAW_KEY=$(az monitor log-analytics workspace get-shared-keys \
--resource-group rg-orders-dev \
--workspace-name log-orders-dev-eastus \
--query primarySharedKey --output tsv)
az containerapp env create \
--name cae-orders-dev \
--resource-group rg-orders-dev \
--location eastus \
--logs-workspace-id "$LAW_ID" \
--logs-workspace-key "$LAW_KEY"The first two commands read the workspace ID and shared key into shell variables. The key is a secret, so never echo it or save it in a script. The last command creates the environment cae-orders-dev and sends logs to your workspace. Note that this is a basic public environment, which is fine for a lab. Production usually needs the private networking covered later.
Pro Tip: I once left a shared key in a shell history file that was synced to a team drive. Now I read keys into variables inside a script, and I rotate the workspace key if I suspect exposure.
Deploy Your Container With a Managed Identity
Pushing a registry password into your app is the mistake I see most. Use a managed identity instead. It is an identity that Azure manages for a resource, so your app authenticates without a stored secret. See what a managed identity is in Azure for the basics.
Build and Store the Image
Create an Azure Container Registry (ACR), a private store for your container images, and build there.
az acr create \
--resource-group rg-orders-dev \
--name <registry-name> \
--sku Basic
az acr build \
--registry <registry-name> \
--image orders-api:1.0.0 \
--file Dockerfile .The first command creates the registry. The Basic SKU suits dev; production often needs Premium for private endpoints and geo-replication. az acr build builds the image inside Azure from your local Dockerfile and pushes it. That means no Docker install is needed on build agents. Use a version tag like 1.0.0, not latest, so you know exactly what runs.
Create the Container App
az containerapp create \
--name ca-orders-api \
--resource-group rg-orders-dev \
--environment cae-orders-dev \
--image <registry-name>.azurecr.io/orders-api:1.0.0 \
--registry-server <registry-name>.azurecr.io \
--registry-identity system \
--target-port 8080 \
--ingress external \
--cpu 0.5 --memory 1.0Gi \
--min-replicas 0 --max-replicas 5This command creates the app. The --registry-identity system flag tells it to pull images using its own system-assigned identity, with no password. The identity needs the AcrPull role on the registry, and the CLI tries to assign it.
If your account cannot create role assignments, assign AcrPull manually. --target-port 8080 must match the port your app listens on. --ingress external gives the app a public HTTPS address, which exposes it to the internet. Use internal for services that only other apps should call. --min-replicas 0 allows scale to zero, and --max-replicas 5 caps cost.
Grant access using RBAC, which assigns permissions through roles instead of broad admin rights. If it is new to you, read what RBAC is.
Pro Tip: I always set
--max-replicason day one. A retry storm from a buggy client once scaled a test app to its default limit, and the bill reminded us.
Store Secrets in Azure Key Vault
Apps still need secrets, like a database connection string or a partner API key. Azure Key Vault stores secrets, keys, and certificates securely. Learn the model in how Azure Key Vault works. Never put secrets in source code, Dockerfiles, plain environment variables, spreadsheets, or shared files.
Container Apps can reference a Key Vault secret directly, using an identity:
az containerapp secret set \
--name ca-orders-api \
--resource-group rg-orders-dev \
--secrets "db-connection=keyvaultref:<key-vault-secret-uri>,identityref:<identity-resource-id>"
az containerapp update \
--name ca-orders-api \
--resource-group rg-orders-dev \
--set-env-vars "DB_CONNECTION=secretref:db-connection"The first command defines an app secret named db-connection that points to a Key Vault secret. The identityref part names the managed identity that reads it, and that identity needs the Key Vault Secrets User role.
The second command exposes it to the container as an environment variable through secretref. The value is never written in your template or repo. Secret changes apply to new revisions, so restart or create a new revision after rotation.
Better still, skip the password completely. If your app talks to Azure SQL or Storage, use the managed identity and Microsoft Entra ID authentication, which is Azure’s identity service for sign-in and access control.
Pro Tip: In my experience, most Key Vault failures are permissions or network blocks, not typos. I test the identity’s access first, before I blame the app code.
Lock Down Azure Networking
By default, a Container Apps environment is public. That is fine for a demo and risky for internal systems. Use a virtual network (VNet), a private network boundary for Azure resources. Learn the concept in what an Azure virtual network is.
Plan the subnet before you create the environment, because the subnet cannot be changed afterward, and size requirements differ by environment type. Then create an internal environment:
az containerapp env create \
--name cae-orders-prod \
--resource-group rg-orders-prod \
--location eastus \
--infrastructure-subnet-resource-id <subnet-resource-id> \
--internal-only