From Confusion to Clarity: Deploying a .NET Web API on Azure with ACR

A Deep Dive into Azure Web Apps, Docker, and Debugging Deployment Headaches
Introduction App#
This post walks through a proof of concept (POC) where I deploy a .NET Web API in a Docker container, hosted on Azure Web Apps, using Azure Container Registry (ACR), and connected to an Azure SQL Database via Key Vault & App Insights.
I did this as part of my AZ-204 certification prep, but more than that, I wanted to fully understand the deployment process manually before automating it. That means:
- Deploying each Azure resource manually with Azure CLI
- Building and pushing a Docker image to Azure Container Registry
- Deploying the containerized app to an Azure Web App
- Configuring managed identities for secure connections to Key Vault and SQL
Sounds simple? Well, it wasn’t. I ran into all kinds of issues—mostly Docker confusion, web app failures, and port mismatches. I’ll share my debugging process and lessons learned so future-me (and anyone reading this) can avoid the same headaches.
TL;DR – Want Just the Step-by-Step Guide?#
If you just want the step-by-step commands to deploy everything, jump to the bottom for the TL;DR Deployment Guide.
Why This POC? (Or, How I Ended Up Debugging Ports for Hours)#
At first, I thought this would be straightforward. I’d create my Azure resources, build and push a Docker image, and everything would just work. Spoiler: It didn’t.
The biggest pain points were:
- Docker refusing to build properly
- Confusion around images, containers, and ACR
- The web app not booting after deployment (turned out to be a port issue)
These challenges forced me to truly understand how Azure Web Apps handle Docker containers and why setting the correct ports is crucial. Now that I’ve fought through these issues, I can confidently repeat this process anytime.
Learned While Deploying#
Docker and ACR Confusion#
Before this, I thought I understood Docker. Turns out, I only knew the surface-level basics.
Key takeaways:
- Docker images are just blueprints for containers.
- Containers are running instances of those images.
- ACR (Azure Container Registry) is just a repo for container images.
The Docker build → push → run process finally clicked for me:
# Build Docker image
docker build \
-f ./MyWebApiApp/Dockerfile \
-t $ACR_LOGIN_SERVER/$IMAGE_NAME:$IMAGE_TAG .
# Push to ACR
docker push $ACR_LOGIN_SERVER/$IMAGE_NAME:$IMAGE_TAG
# Run locally for testing
docker run --name mywebapi \
-p 8080:8080 $ACR_LOGIN_SERVER/$IMAGE_NAME:$IMAGE_TAG
I also wasted a ton of time running Docker builds from the wrong directory. If the Dockerfile
references files one level down, you have to build from the correct location.
Debugging a Web App That Wouldn’t Boot#
So, I finally got my image built and pushed to ACR. I deployed the Web App, and… nothing.
Checked the logs and saw this:
Now listening on port 80
But locally, I was using port 8080.
Azure Web Apps need an explicit port setting.
Once I set this:
az webapp config appsettings set --name $APP_NAME \
--resource-group $RESOURCE_GROUP \
--settings WEBSITES_PORT=8080
Everything just worked.
Why A Cloud Guru’s 4-Hour Sandbox is Actually a Good Thing#
At first, I was annoyed by the fact that I only had 4 hours in each sandbox session. But now I realize:
- It forces repetition, which means I actually remember the process.
- If I don’t fully understand something, I have to re-deploy it multiple times.
- It’s just like practicing at the gym—each session builds muscle memory.
Next Steps#
Right now, my web app isn’t actually using:
- Azure SQL for data
- Key Vault for secrets
- App Insights for logging
The next step is connecting everything so the web app can securely interact with these services.
Also, I haven’t touched Bicep yet. My plan is to get everything working manually before automating deployments.
TL;DR Deployment Guide#
This is the full step-by-step guide for when I need to recreate this POC quickly.
Step 1: Prerequisites and Define Environment Variables#
Install Required Tools#
- Azure CLI →
brew install azure-cli
- Docker →
brew install --cask docker
Set these in your terminal before running commands:#
# Azure Resource Group
RESOURCE_GROUP="## 1-83580a9f-playground-sandbox"
# Azure Container Registry (ACR)
ACR_NAME="mywebapiacrsb"
ACR_LOGIN_SERVER="$ACR_NAME.azurecr.io"
# Azure App Service
APP_SERVICE_PLAN="my-webapi-app-plan-sb"
APP_NAME="my-webapi-app-sb"
LOCATION="eastus"
# Azure SQL Server & Database
SQL_SERVER_NAME="mywebapisqlsb"
SQL_DATABASE_NAME="MyWebApiDb"
# Azure Key Vault
KEY_VAULT_NAME="mywebapikv1sb"
# Azure Application Insights
APP_INSIGHTS_NAME="my-webapi-app-ai-sb"
# Docker Image Details
IMAGE_NAME="mywebapiapp"
IMAGE_TAG="v1"
Step 2: Login and Set Resource Group and Subscription#
Log into Azure#
az login
az group create --name $RESOURCE_GROUP \
--location $LOCATION
az account set --subscription "<SUBSCRIPTION_ID>"
Step 3: Create & Push to App Container Registry#
Create App Container Registry and Login#
az acr create --resource-group $RESOURCE_GROUP \
--name $ACR_NAME \
--sku Basic --admin-enabled true
az acr login --name $ACR_NAME
Build Docker Image#
docker build \
-f ./MyWebApiApp/Dockerfile \
-t $ACR_LOGIN_SERVER/$IMAGE_NAME:$IMAGE_TAG .
Push Docker Image to ACR#
docker push $ACR_LOGIN_SERVER/$IMAGE_NAME:$IMAGE_TAG
Step 5: Create & Deploy to Web App#
Create App Service Plan#
az appservice plan create --name $APP_SERVICE_PLAN \
--resource-group $RESOURCE_GROUP \
--sku B1 --is-linux
Create Web App#
az webapp create --name $APP_NAME \
--resource-group $RESOURCE_GROUP \
--plan $APP_SERVICE_PLAN \
--deployment-container-image-name $ACR_LOGIN_SERVER/$IMAGE_NAME:$IMAGE_TAG
Set Setting for Web App#
az webapp config container set --name $APP_NAME \
--resource-group $RESOURCE_GROUP \
--docker-custom-image-name $ACR_LOGIN_SERVER/$IMAGE_NAME:$IMAGE_TAG \
--docker-registry-server-url https://$ACR_LOGIN_SERVER \
--docker-registry-server-user $(az acr credential show --name $ACR_NAME --query "username" --output tsv) \
--docker-registry-server-password $(az acr credential show --name $ACR_NAME --query "passwords[0].value" --output tsv)
Fix the Web App Not Booting#
az webapp config appsettings set --name $APP_NAME \
--resource-group $RESOURCE_GROUP \
--settings WEBSITES_PORT=8080
Step 6: Test Web App#
Restart App Service:#
az webapp restart --name $APP_NAME \
--resource-group $RESOURCE_GROUP
Get the Web App URL:#
az webapp show --name $APP_NAME \
--resource-group $RESOURCE_GROUP \
--query "defaultHostName" -o tsv
Access in your browser:#
https://<APP_NAME>.azurewebsites.net
Step 7: Create Other Resources#
Create Azure SQL Server & Database#
az sql server create --name $SQL_SERVER_NAME \
--resource-group $RESOURCE_GROUP \
--location $LOCATION \
--admin-user sqladmin \
--admin-password "YourStrongls #Password"
az sql db create \
--resource-group $RESOURCE_GROUP \
--server $SQL_SERVER_NAME \
--name $SQL_DATABASE_NAME \
--service-objective Basic
Set Up Key Vault & Managed Identity#
az keyvault create --name $KEY_VAULT_NAME \
--resource-group $RESOURCE_GROUP \
--location $LOCATION
az keyvault set-policy --name $KEY_VAULT_NAME \
--object-id $(az webapp identity assign --name $APP_NAME --resource-group $RESOURCE_GROUP --query principalId --output tsv) \
--secret-permissions get list
Enable App Insights#
az monitor app-insights component create \
--app $APP_INSIGHTS_NAME \
--location $LOCATION \
--resource-group $RESOURCE_GROUP
Step 8: Clean Up Resources (When Sandbox Expires)#
az group delete --name $RESOURCE_GROUP --yes --no-wait
Final Thoughts#
This POC wasn’t easy, but I now have a repeatable process for deploying a secure web app in Azure.
Next, I need to:
- Connect the web app to actually use SQL, Key Vault, and App Insights.
- Automate this setup with Bicep.
If future-me is reading this: Don’t forget to set the damn port.
Now that we have the Web App deployed, the next step is connecting it to Azure SQL Database.
- We’ll configure the database, set up firewall rules, and verify connectivity.
- Then, we’ll integrate it into the Web API, seed data, and expose endpoints.
Continue the setup here: Connecting a .NET API to Azure SQL – The Next Step