Skip to main content

Deploy a .NET App to Azure Container Apps — Part 4: Push Your Image to the Registry

· 8 min read
Jagdish Kumawat
Founder @ Dewiride

This is the shortest part of the series and the one most likely to save you an afternoon. Pushing an image is three commands — but if you're on an Apple Silicon Mac, one missing flag produces a container that builds perfectly, pushes perfectly, and then refuses to start on Azure.

This is Part 4 of a 6-part series.

  1. Part 1: Build the App
  2. Part 2: Containerize It with Docker
  3. Part 3: Resource Group and Container Registry
  4. Part 4: Push Your Image to the Registry (you are here)
  5. Part 5: Deploy to Azure Container Apps
  6. Part 6: Logs, Scaling, Cost and Cleanup

What you need before starting

The registry from Part 3, Docker Desktop running, and the Azure CLI.

Install the CLI from Microsoft's install guide, then check it:

Terminal
az version
Output
{
"azure-cli": "2.89.0",
"azure-cli-core": "2.89.0",
"azure-cli-telemetry": "1.1.0",
"extensions": {}
}

Step 1: Sign in to Azure

Terminal
az login

A browser window opens. Sign in with the same account you used for the portal. The terminal then lists your subscriptions.

If you have more than one subscription, set the right one as active:

Terminal
az account set --subscription "Your Subscription Name"

Confirm which one is active:

Terminal
az account show --query "{name:name, id:id}" -o table
Output
Name Id
---------------- ------------------------------------
My Subscription 00000000-0000-0000-0000-000000000000

Step 2: Sign in to your registry

Terminal
az acr login --name acrdotnetdemo2026

Use your own registry name.

Output
Login Succeeded

This hands Docker a short-lived token based on your Azure identity. No password is stored, and nothing had to be enabled on the registry — this is why the admin user from Part 3 stays switched off.

Step 3: The trap — check your chip

Before pushing, run this:

Terminal
uname -m

If it says x86_64, you're on Intel or AMD and can skip ahead.

If it says arm64 — every Apple Silicon Mac, M1 through M4 — read this section carefully.

Look at the image you built in Part 2:

Terminal
docker inspect hello-container-app:1.0.0 --format 'Architecture={{.Architecture}}'
Output
Architecture=arm64

Your image is built for ARM, because your laptop is ARM. Azure Container Apps runs on x86-64 servers. An ARM image on an x86 host doesn't produce a helpful error — it produces this, buried in the logs, after everything else appeared to work:

What you'd see on Azure
exec /usr/bin/dotnet: exec format error

The image built. The push succeeded. The portal showed no errors. The container just refuses to start, because the CPU instructions inside it are for a different processor family.

Industry practice: always state the target platform explicitly

This bites people constantly, and the symptom appears far away from the cause. Never rely on your laptop's architecture matching your server's.

Build with an explicit --platform whenever the image is going somewhere other than your own machine. It costs nothing to be explicit and it removes an entire class of confusing failure.

Step 4: Build for Azure and push

One command builds for the correct architecture and pushes it:

The terminal commands to sign in to the registry and build a linux amd64 image with docker buildx, then push it

Terminal
docker buildx build --platform linux/amd64 \
-t acrdotnetdemo2026.azurecr.io/hello-container-app:1.0.0 \
--push .

What changed from Part 2:

  • buildx instead of plain build — Docker's builder that can target other architectures.
  • --platform linux/amd64 — build for Azure's CPU family, not your laptop's.
  • The image name now starts with your registry address. That prefix is what tells Docker where to send it.
  • --push uploads it as part of the build.

On an ARM Mac this is slower than a native build, because Docker emulates an x86 CPU to compile. A minute or two is normal.

Output
=> pushing layer 296c0858796d 82.71MB
=> pushing layer 42724e448bf4 78.16MB
=> pushing manifest for acrdotnetdemo2026.azurecr.io/hello-container-app:1.0.0
=> DONE 38.1s
Industry practice: version your tags, never deploy :latest

The tag here is 1.0.0, not latest.

latest is not a version — it's a moving pointer. If you deploy it, you can't tell which build is actually running, you can't roll back to "the one from Tuesday", and two servers pulling latest an hour apart can end up on different code.

Azure Container Apps builds revisions around image tags. Give each build a real version and rollback becomes a click. Deploy latest and you've thrown that away.

Step 5: Confirm it arrived

From the terminal:

Terminal
az acr repository show-tags \
--name acrdotnetdemo2026 \
--repository hello-container-app \
-o table
Output
Result
--------
1.0.0

And confirm the architecture is now correct:

Terminal
az acr manifest list-metadata \
--registry acrdotnetdemo2026 \
--name hello-container-app \
--query "[].{tag:tags[0], arch:architecture, os:operatingSystem}" -o table
Output
Tag Arch Os
----- ------ -----
1.0.0 amd64 linux

amd64 — that's what you want. If it says arm64, the --platform flag didn't take effect; rebuild with it.

You can also see it in the portal. Open your registry and choose Repositories in the left menu, then click hello-container-app:

The Azure portal showing the hello-container-app repository in the registry with tag 1.0.0 and its digest

One repository, one tag, one manifest. Your image is in Azure.

Common Mistakes

MistakeWhat you'll seeFix
Omitting --platform on an ARM Macexec format error on Azure; nothing wrong locallyRebuild with --platform linux/amd64
Forgetting the registry prefix on the tagDocker tries to push to Docker Hub and gets deniedTag as <registry>.azurecr.io/<image>:<tag>
Uppercase in the image nameinvalid reference formatImage names must be lowercase
Pushing :latestNo way to identify or roll back a deployUse real version numbers
docker push without az acr login firstunauthorized: authentication requiredSign in to the registry first

Troubleshooting

ProblemCauseFix
unauthorized: authentication requiredToken expired (they're short-lived)Run az acr login again
denied: requested access to the resource is deniedWrong registry name, or no permissionCheck the name; you need AcrPush or Contributor
Cannot connect to the Docker daemonDocker Desktop not runningStart it
buildx: command not foundVery old DockerUpdate Docker Desktop; buildx is included
Push is extremely slowFirst push uploads every layerLater pushes only send changed layers

FAQ

Why not just docker push? You can — docker tag then docker push works. But that pushes whatever architecture you built, which on an ARM Mac is the wrong one. buildx --platform ... --push does the right thing in a single step.

Can I build for both architectures? Yes: --platform linux/amd64,linux/arm64 produces a multi-architecture image and each host pulls the right variant. It roughly doubles build time; for this series one architecture is enough.

Do I need Docker at all? Not strictly. az acr build uploads your source and builds it inside Azure, which sidesteps the architecture issue entirely and needs no local Docker. This series builds locally so you can test before you deploy.

How do I update the app later? Change the code, build and push with a new tag (1.0.1), then point the container app at the new tag. Part 6 covers revisions.

Is my image public now? No. ACR is private. Only identities you authorise can pull it — which is exactly what Part 5 sets up.

What's Next

Your image is sitting in Azure, built for the right architecture, waiting. In Part 5 you'll give it somewhere to run and get your public HTTPS URL.


Previous: ← Part 3: Resource Group and Container Registry Next: Part 5: Deploy to Azure Container Apps →

Additional Resources

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.