DEV Community

Raphael Olaniyi
Raphael Olaniyi

Posted on

Master Cloud Deployment: Managing Container Images with Docker, Azure Container Registry, and Azure Container Apps

In modern cloud-native engineering, building an application locally is only the first step. To run reliably, an application must be packaged, versioned, stored securely, and prepared for scalable execution in the cloud.

This article is the continuation of my previous article, "From GitHub to Container: Running a Node.js App (Wonderkid App) Locally and Packaging It with Docker." Here we take the container image we built and move it to the cloud: tagging it, storing it in a private Azure Container Registry (ACR), and running it on Azure Container Apps (ACA). The first two sections revise the key concepts from the previous article before we move on to the Azure workflow.

Prerequisites: Docker Desktop (running), the Azure CLI, an active Azure subscription, and the Docker image from Part 1.


1. Docker Image Creation and Tagging

Before an application can move to the cloud, it must be packaged as a container image using a Dockerfile.

Building an Image

A Docker image is built in layers, one for each instruction in the Dockerfile (such as FROM, WORKDIR, COPY, RUN, EXPOSE, and CMD). To build an image locally, run:

docker build -t <image-name>:<version> .
Enter fullscreen mode Exit fullscreen mode
  • -t flag: assigns a name and version tag to the image.
  • . (dot): sets the build context to the current directory, so Docker uses the files there.

Apple Silicon (M1/M2/M3) users: Azure Container Apps runs Linux AMD64 containers. If you build on an ARM-based Mac, add the platform flag so the image runs in Azure:

docker build --platform linux/amd64 -t <image-name>:<version> .

Image Tagging Strategies

Tags let you track and manage each iteration of your application.

  • Use explicit versions. Tags such as 1.0.0 or 2.0.0 are better than relying on latest. Because latest is just a default tag that gets reassigned, it hides which revision is actually running and makes rollbacks unpredictable.
  • Tags can point to a registry. The docker tag command doesn't rename an image. It adds an additional reference to the same image. When that reference includes a registry address (for example myregistry.azurecr.io/wonderkids:1.0.0), Docker knows where to push the image.

2. Public vs. Private Container Registries

Once built, images need a central place to be stored, a container registry, so they can be pulled by any environment that needs to run them.

Public Registries (e.g., Docker Hub)

  • Overview: Docker Hub is the largest public image library, and the default registry for Docker.
  • Best for: open-source projects, public distribution, and standard base images (Node.js, Alpine, Ubuntu).
  • Limitation: Anything pushed to a public repository is visible to everyone, including your application code, configuration baked into the image, and any secrets accidentally included. Docker Hub does support private repositories, but they come with plan limits and sit outside your Azure security boundary.

Private Registries (e.g., Azure Container Registry)

  • Overview: Azure Container Registry (ACR) is a managed, private registry hosted in Azure.
  • Best for: enterprise workloads. It offers role-based access control (RBAC), network isolation options, and integration with Microsoft Entra ID and other Azure services.

3. Deployment Walkthrough: Pushing an Image to Azure Container Registry

The following steps push your local Docker image to ACR using the Azure CLI.

Step 1: Sign in to Azure

az login
Enter fullscreen mode Exit fullscreen mode

This opens a browser window (or uses a device code) to authenticate your Azure CLI session. If you have more than one subscription, select the one you want:

az account set --subscription "<subscription-name-or-id>"
Enter fullscreen mode Exit fullscreen mode

Step 2: Create a resource group

A resource group is a logical container for related Azure resources.

az group create --name <resource-group-name> --location <location>
Enter fullscreen mode Exit fullscreen mode
  • Example locations: eastus, northeurope, australiaeast.
  • Best practice: keeping everything for one project in a single resource group makes cleanup and cost tracking much simpler. Deleting the group removes everything inside it.

group

Step 3: Create the private registry

az acr create --resource-group <resource-group-name> --name <acr-name> --sku Basic --admin-enabled false
Enter fullscreen mode Exit fullscreen mode
  • Registry names must be globally unique, 5 to 50 characters, lowercase letters and numbers only (no hyphens).
  • --sku Basic: the lowest-cost tier, suitable for learning and small projects.
  • --admin-enabled false: disables the shared admin account (this is already the default). Access is controlled through individual identities and Azure RBAC rather than a shared username and password.

create private reg

Step 4: Authenticate Docker to the registry

az acr login --name <acr-name>
Enter fullscreen mode Exit fullscreen mode

This uses your Azure CLI session to give your local Docker engine a short-lived token for the registry. Docker Desktop must be running. Your account needs permission to push images, such as the AcrPush role, or Owner or Contributor on the registry. If you created the registry yourself, you already have access.

acr login

Step 5: Tag the image for the registry

docker tag <local-image-name>:<version> <acr-name>.azurecr.io/<repository-name>:<version>
Enter fullscreen mode Exit fullscreen mode

tag

Step 6: Push the image

docker push <acr-name>.azurecr.io/<repository-name>:<version>
Enter fullscreen mode Exit fullscreen mode

Docker uploads the image layer by layer. Layers that already exist in the registry are skipped, which makes later pushes faster.

Docker push

Step 7: Verify the upload

az acr repository list --name <acr-name> --output table
az acr repository show-tags --name <acr-name> --repository <repository-name> --output table
Enter fullscreen mode Exit fullscreen mode

The first command confirms the repository exists, and the second confirms your version tag is there.

confirmation


4. Scalable Execution: Azure Container Apps (ACA)

With the image stored in ACR, you can run it on Azure Container Apps, a managed serverless platform for containers.

  • Serverless execution: ACA is built on top of Kubernetes, but Azure manages the cluster for you.
  • Less complexity: there are no control planes, nodes, or ingress controllers to manage, and no Kubernetes YAML required to get started.
  • Key benefits:
    • Built-in autoscaling: scales on HTTP traffic, events, or CPU and memory load.
    • Microservice support: internal service discovery, traffic splitting for canary releases, and managed HTTPS ingress.
    • Cost efficiency: apps can scale to zero replicas when idle, so you pay only while they run. This is configurable, since setting a minimum replica count keeps the app always warm.

Deploying the image to Azure Container Apps

1. Install the Container Apps extension and register the providers (one-time setup):

az extension add --name containerapp --upgrade
az provider register --namespace Microsoft.App
az provider register --namespace Microsoft.OperationalInsights
Enter fullscreen mode Exit fullscreen mode

2. Create a Container Apps environment, the secure boundary that hosts your apps:

az containerapp env create --name <environment-name> --resource-group <resource-group-name> --location <location>
Enter fullscreen mode Exit fullscreen mode

3. Create the container app from your ACR image:

az containerapp create \
  --name <app-name> \
  --resource-group <resource-group-name> \
  --environment <environment-name> \
  --image <acr-name>.azurecr.io/<repository-name>:<version> \
  --target-port 3000 \
  --ingress external \
  --registry-server <acr-name>.azurecr.io \
  --registry-identity system
Enter fullscreen mode Exit fullscreen mode
  • --target-port: must match the port your app listens on inside the container (the EXPOSEd port, e.g., 3000).
  • --ingress external: makes the app reachable from the internet over HTTPS.
  • --registry-identity system: gives the app a managed identity to pull from ACR, so no passwords are needed. The CLI assigns the AcrPull role for you, provided your account is allowed to create role assignments.

4. Get the app URL:

az containerapp show --name <app-name> --resource-group <resource-group-name> \
  --query properties.configuration.ingress.fqdn --output tsv
Enter fullscreen mode Exit fullscreen mode

Open the returned address in your browser (prefixed with https://) to see the app running in Azure.

5. Clean up when you're done to avoid unnecessary charges:

az group delete --name <resource-group-name> --yes --no-wait
Enter fullscreen mode Exit fullscreen mode

Summary

Containerizing an application bridges local development and cloud infrastructure. By building versioned images, storing them in a private registry like Azure Container Registry, and running them on a serverless platform like Azure Container Apps, teams can deliver applications that are reliable, secure, and scalable.


Top comments (0)