From Dockerfile to Production : Azure

Spin up a production-ready pipeline with GitHub Actions, and about 30 minutes of elbow grease.
1 · Prerequisites & Quick Overview
You Need | Why |
Azure account (With Subscription) | Spin up SQL, ACR, App Service Plan, and Web App. |
GitHub repo with your .NET 8 Web API | The CI/CD workflow lives here. |
EF Core migrations already created | Workflow auto-applies them during deploy. |
Dockerfile at repo root | Builds the API image. |
Architecture at a glance
GitHub → ACR → App Service (Linux Container) ⟷ Azure SQL Database
↘︎ App Insights ↙︎
2 · Provision Azure SQL Database
2.1 Create SQL Server
Setting | Value |
Resource Group | stocks-api-rg |
Server Name | stocks-sql-server-<unique> |
Auth | SQL authentication |
Admin / Password | ✍️ Save in password manager |
Azure Portal → Create a resource → “SQL Server” → Review + Create.
2.2 Create StocksDb
Database
Open your new SQL Server → Databases → + New Database
Tier 🡒 Basic, 5 DTUs, 2 GB.
2.3 Networking / Firewall
Action | Click |
Allow Azure services | Yes |
Add client IPv4 | Add client IP |
Save | 💾 |
2.4 Get Connection String
SQL DB → Connection strings → copy ADO.NET string → replace
{user}
/{password}
.
3 · Create App-Service Resources
3.1 App Service Plan
Setting | Value |
Plan name | stocks-api-plan |
OS | Linux |
Tier | Basic B1 |
Region | Same as SQL |
3.2 Azure Container Registry (ACR)
Setting | Value |
Name | stocksacr<unique> |
SKU | Basic |
Admin user | Enable |
3.3 Web App for Containers
Setting | Value |
Name | stocks-api-<unique> |
Publish | Docker Container |
Plan | stocks-api-plan |
Image Source | ACR → stocks-api:latest (workflow pushes this) |
4 · Configure Web-App Settings
Configuration → Application settings
ASPNETCORE_ENVIRONMENT
=Production
Connection strings
Name
DefaultConnection
Paste SQL string
Type
SQLAzure
Save & Restart.
5 · Generate & Store Secrets
5.1 Service Principal (AZURE_CREDENTIALS)
az login
SUBID=$(az account show --query id --output tsv)
az ad sp create-for-rbac \
--name "github-actions" \
--role contributor \
--scopes /subscriptions/$SUBID/resourceGroups/stocks-api-rg \
--sdk-auth
Copy the JSON output → save as AZURE_CREDENTIALS
secret.
5.2 ACR Credentials
Portal → ACR → Access keys → copy:
GitHub Secret | Value |
REGISTRY_LOGIN_SERVER | stocksacr.azurecr.io |
REGISTRY_USERNAME | (username) |
REGISTRY_PASSWORD | (password) |
5.3 DB Connection
Secret | Value |
DB_CONNECTION_STRING | (ADO.NET string) |
Add all secrets under Repo → Settings → Secrets → Actions.
6 · Step 5 — GitHub Actions Workflow
Build and Deploy
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
- run: dotnet tool install -g dotnet-ef
- run: dotnet restore
- run: dotnet build --no-restore -c Release
- run: dotnet test --no-build -c Release
- name: Update Database
if: github.event_name != 'pull_request'
run: dotnet ef database update -c Release
env:
ConnectionStrings__DefaultConnection: ${{ secrets.DB_CONNECTION_STRING }}
- uses: docker/setup-buildx-action@v2
- name: Login to ACR
uses: docker/login-action@v2
with:
registry: ${{ secrets.REGISTRY_LOGIN_SERVER }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build & Push Image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: |
${{ secrets.REGISTRY_LOGIN_SERVER }}/stocks-api:${{ github.sha }}
${{ secrets.REGISTRY_LOGIN_SERVER }}/stocks-api:latest
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deploy to Web App
uses: azure/webapps-deploy@v2
with:
app-name: stocks-api-<unique>
images: ${{ secrets.REGISTRY_LOGIN_SERVER }}/stocks-api:${{ github.sha }}
- run: az logout
🔑 Don’t forget: Dockerfile must expose port 80
ENV ASPNETCORE_URLS=http://+:80
EXPOSE 80
7 · Ship & Verify
git add .
git commit -m "CI/CD: Azure SQL + ACR deploy"
git push origin main
GitHub → Actions → watch the build.
Upon success, open:
https://stocks-api-<unique>.azurewebsites.net/swagger/index.html
Test endpoints — your API is live!
8 · Observability with App Insights
Web App → Application Insights → Turn on.
Live Metrics, traces, and failure sampling appear automatically.
9 · Troubleshooting Playbook
Symptom | Likely Cause | Quick Fix |
503 / 504 | App listens on wrong port | Ensure ASPNETCORE_URLS= http://+:80 |
EF Core migration fails | Bad DB_CONNECTION_STRING | Check secret & firewall settings |
Docker push denied | Wrong ACR creds | Re-generate REGISTRY_PASSWORD |
Service principal auth error | Insufficient RBAC scope | Re-create SP with correct RG scope |
Subscribe to my newsletter
Read articles from Sagar HS directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Sagar HS
Sagar HS
Software engineer with 4 + years delivering high-performance .NET APIs, polished React front-ends, and hands-off CI/CD pipelines. Hackathon quests include AgroCropProtocol, a crop-insurance DApp recognised with a World coin pool prize and ZK Memory Organ, a zk-SNARK privacy prototype highlighted by Torus at ETH-Oxford. Recent experiments like Fracture Log keep me exploring AI observability.