Azure Container Apps (ACA) is a managed container platform which abstracts away the infrastructure layer. You can run complex distributed applications with ACA with high-value features out of the box, including HTTPS provisioning, auto-scale, scale to zero, turnkey authentication, observability and more. ACA runs standard Docker container images - currently limited to Linux on Intel - and uses the power of Kubernetes under the hood, wrapped in a much simpler user experience.
In these exercises we’ll start with a simple distributed app running across two containers and gradually extend it with ACA features, including end-user authentication, encrypted traffic between the components, and a simple approach to building and deploying apps from source.
There is a lot of content between this module and the ACA - Security module. ACA is one of the more feature-rich services and you’ll need to spend quite a bit of time with it to be confident of what it can do and how to use it.
Implement Azure Container Apps | Microsoft Learn |
az containerapp
commandsIn the Portal create a new Container App. Explore the tabs for the new service; you can:
Create a new Resource Group for the lab, change the region if you like:
az group create -n labs-aca --tags course=az204 -l eastus
ACA is a relatively new serivce - ensure it’s enabled for your subscription, and update your command line extension:
az provider register -n Microsoft.App --wait
az provider register -n Microsoft.OperationalInsights --wait
az extension add --name containerapp --upgrade
Now create a Container App Environment. This is a grouping of Container Apps which can communicate internally and share common features like secrets:
az containerapp env create --name rng -g labs-aca
You’ll see this also create a Log Analytics Workspace. This is a service for collecting, storing and querying log data and metrics. ACA automatically wires up container logs to write to Log Analytics.
🧭 Explore your environment in the Azure Portal - from the ACA environments list. Here are some key points:
Now you can use az containerapp create
to create a new Container App in the environment.
📋 Create a Container App called rng-api
using the image ghcr.io/eltons-academy/rng-api:2025
. This is a REST API - you should create it to be externally available for HTTP traffic, mapping to port 8080
inside the container.
Not sure how?</summary>
Start with the help:
az containerapp create --help
For a new Container App you need to specify the environment and image. To get public networking, specify external ingress and set the target port:
az containerapp create --name rng-api --environment rng -g labs-aca --image ghcr.io/eltons-academy/rng-api:2025 --target-port 8080 --ingress external
</details>
When your Container App is running it will be allocated a public DNS name. You can query the resource to print just the domain name, and store the result in a variable:
$RNG_API=$(az containerapp show -n rng-api -g labs-aca --query 'properties.configuration.ingress.fqdn' -o tsv)
This is a REST API which generates a random number. Test it with an HTTP request and you should see a number:
curl "https://$RNG_API/rng"
Note the HTTPS - ACA provisions and applies a TLS cert along with the domain entry
The ACA archiecture has many layers - your container is one replica running in one revision in one app in the container app environment. But there are simple commands for everyday management tasks.
📋 Print the logs from your API container.
Not sure how?</summary>
Start with the help to see what commands are available:
az containerapp --help
There is a logs
command group:
az containerapp logs --help
You can specify the revision and replica to fetch the logs from, but the default will pick one replica from the active revision:
az containerapp logs show -n rng-api -g labs-aca
</details>
🧭 Explore your container app in the Azure Portal - from the ACA list. Here are some key points:
This is just one part of the full solution, next we’ll add a Web UI container.
ACA is designed for distributed applications where each component runs in its own Container App in the same Environment. The service has features to ensure containers can communicate securely.
📋 Create a new Container App in the same Environment to run the web UI. Call it rng-web
and use the image ghcr.io/eltons-academy/rng-web:2025
. The container listens on port 8080 and it should be publicly available. Print the domain address so you can try the app.
Not sure how?</summary>
This is pretty much the same command - only the Docker image and the name changes. If you add the --query
parameter to the create command, the output will just be the field(s) you specify.
az containerapp create --name rng-web --environment rng -g labs-aca --image ghcr.io/eltons-academy/rng-web:2025 --target-port 8080 --ingress external --query properties.configuration.ingress.fqdn
</details>
Browse to the domain and you should see a simple web page with a Go button. Click the button and the web app tries to fetch a random number from the API.
When you try it you’ll see an error message. The URL the web app is using for the API is incorrect.
We need to change the config setting in the web app. These components are both .NET apps, using JSON files and enviroment variables for configuration. In a real app the configuration options would be documented somewhere, or you’d have to ask developers or dig into the source code. In this case there is a default setting in the Docker image.
# get your own copy of the image:
docker pull ghcr.io/eltons-academy/rng-web:2025
# inspect it to show the details
docker image inspect ghcr.io/eltons-academy/rng-web:2025
The API URL which the web app is trying to use is set in the
RngApi__Url
environment variable.
📋 Print the details of the web Container App and look at the environment variables which have been set.
Not sure how?</summary>
The usual show
command prints all the resource details:
az containerapp show -g labs-aca -n rng-web
Environment variables are set in the templated container, in the env
field:
az containerapp show -g labs-aca -n rng-web --query 'properties.template.containers[0].env'
</details>
There are no environment variables set in the container, which means it’s using the default from the Docker image.
📋 Update the web Container App to set an environment variable called RngApi__Url
with the full path to the RNG API endpoint - including the HTTPS scheme and the /rng
path.
Not sure how?</summary>
Environment variables can be set when you create a Container App, or when you update it.
The update
command help shows some useful examples:
az containerapp update --help
To set the environment variable run:
az containerapp update -g labs-aca -n rng-web --set-env-vars "RngApi__Url=https://$RNG_API/rng"
</details>
When you make a change to a container app it gets implemented as a new revision, and when the replica(s) in the revision are ready then ingress traffic is shifted to them. Check the revision list to see when the new revision is receiving all traffic:
az containerapp revision list -g labs-aca -n rng-web -o table
Now if you try the web app again, it will connect to the API and work correctly.
🧭 Explore your web container app in the Azure Portal and open Application…Revisions and replicas
So far we’ve been using my public images to run containers. ACA also has support for building container images from source - like we did with Docker Compose in the containers module.
ACA has Docker Compose support so you can run a cloud version of docker compose build
. You can start from scratch to build and deploy an ACA app from source:
This is a very quick way to prototype your app running in Azure. You can use the exact same Compose file you use locally:
📋 Use a containerapp compose
command to deploy a new version of the random number app from the Compose file docker-compose-build.yml
in the folder src/rng
. Make sure to use a new Container App Environment so you can run alongside your existing app.
Not sure how?</summary>
There is only one compose
command :)
az containerapp compose --help
az containerapp compose create --help
You need to be in the correct folder for the source, then run the create
command with a new environment name, and the name of the Compose file:
cd src/rng
az containerapp compose create -g labs-aca --environment rng2 -f docker-compose-build.yml
</details>
It will take a few minutes to create all the services, build the images and start the app. When it’s done you can find the address of the web container:
az containerapp show -g labs-aca -n numbers-web --query 'properties.configuration.ingress.fqdn'
You can browse to check out the app - but think about the extra configuration we did for the previous deployment. Will the app work?
No, it doesn’t work :)
🧭 Explore your new container apps to track down the problem:
You’ll see the web app has an environment variable set already which it copied from the Compose file, but the URL is wrong (it’s configured for local running with Docker Compose). Also the API has external ingress set up which we don’t want, it should be an internal component in ACA.
📋 Use containerapp update
commands to get the new deployment working - set the API to use internal ingress, mapping HTTP port 80
to port 8080
inside the container, and change the environment variable to the correct URL in the web app.
Not sure how?</summary>
These are the same steps we did with the other apps. Note the new names: numbers-web
and numbers-api
, which come from the Compose file service names.
az containerapp ingress update -g labs-aca -n numbers-api --type internal --transport http --target-port 8080 --allow-insecure true -o table
az containerapp update -g labs-aca -n numbers-web --set-env-vars "RngApi__Url=http://numbers-api/rng"
</details>
Now your app is running correctly. This sort of post-deployment config cleanup is very common - and easy to automate - but if your Compose model is configured differently you might be lucky and get your app working first time.
Check the Portal for your original random number app deployment (the Container Apps called rng-api
and rng-web
) and you’ll see there are no replicas running for the active revision. That’s because the default settings are to scale to zero when there is no traffic for a period.
Browse to the web app and that will trigger a replica to be created - but you’ll find there’s a delay loading the website while the container comes online. Git the Go button and there’s another delay while the API replica is created and that container comes online.
You shouldn’t really use the defaults for things like scaling rules and compute sizes which have a direct impact on cost and performance. Update the original container app so that:
If there’s a load testing tool you’re familiar with, you can use that to test the scale rules.
You can delete the RG for this lab to remove all the resources, including the registry and containers:
az group delete -y --no-wait -n labs-aca