App Service Deployment
Deploy web applications to Azure App Service - Microsoft's fully managed platform for web apps and APIs.
What you'll accomplish
Your web application running on Azure App Service with a public URL
Prerequisites
- Azure account with subscription
- Azure CLI installed
- Web application code
What is Azure App Service?
App Service is Azure's fully managed platform for hosting web applications:
- Fully managed - No server maintenance
- Auto-scaling - Scale based on demand
- Built-in CI/CD - Deploy from Git, GitHub, Azure DevOps
- SSL included - Free managed certificates
- Multiple runtimes - Node.js, Python, .NET, Java, PHP
Your Code → App Service → Azure Infrastructure
│
├── Auto-scaling
├── Load balancing
├── SSL/TLS
└── Monitoring
App Service vs Other Options
| Service | Best For | Containers |
|---|---|---|
| App Service | Web apps, APIs | Optional |
| Container Apps | Microservices | Required |
| Functions | Event-driven | Optional |
| VMs | Full control | Optional |
Note
App Service is the recommended starting point for most web applications on Azure.
Step 1: Create an App Service Plan
An App Service Plan defines the compute resources for your apps.
Pricing Tiers
| Tier | Use Case | Features |
|---|---|---|
| F1 (Free) | Testing | 60 min/day, no custom domains |
| D1 (Shared) | Dev/test | Custom domains, 240 min/day |
| B1 (Basic) | Light production | Custom domains, SSL, 1.75 GB RAM |
| S1 (Standard) | Production | Auto-scale, slots, backups |
| P1V3 (Premium) | High performance | More CPU/RAM, VNet |
Create Plan via CLI
bash# Create Linux App Service Plan az appservice plan create \ --name myapp-plan \ --resource-group myapp-rg \ --sku B1 \ --is-linux \ --location eastus # For Windows apps, omit --is-linux az appservice plan create \ --name myapp-plan \ --resource-group myapp-rg \ --sku B1 \ --location eastus
Step 2: Create the Web App
List Available Runtimes
bash# Linux runtimes az webapp list-runtimes --os-type linux # Windows runtimes az webapp list-runtimes --os-type windows
Common runtimes:
- Node.js:
NODE:20-lts,NODE:18-lts - Python:
PYTHON:3.12,PYTHON:3.11 - .NET:
DOTNETCORE:8.0,DOTNETCORE:7.0 - Java:
JAVA:17-java17,JAVA:11-java11 - PHP:
PHP:8.3,PHP:8.2
Create Web App
bashaz webapp create \ --name myapp-unique-name \ --resource-group myapp-rg \ --plan myapp-plan \ --runtime "NODE:20-lts"
Note
The app name must be globally unique - it becomes myapp-unique-name.azurewebsites.net.
Verify Creation
bash# Get app URL az webapp show \ --name myapp-unique-name \ --resource-group myapp-rg \ --query defaultHostName \ --output tsv # Open in browser az webapp browse --name myapp-unique-name --resource-group myapp-rg
Step 3: Deploy Your Application
Option A: Quick Deploy with az webapp up
The simplest deployment method:
bash# Navigate to your project directory cd /path/to/your/app # Deploy (creates plan and app if needed) az webapp up \ --name myapp-unique-name \ --resource-group myapp-rg \ --runtime "NODE:20-lts" \ --sku B1 \ --location eastus
Option B: ZIP Deployment
bash# Create ZIP of your app zip -r app.zip . -x "node_modules/*" -x ".git/*" # Deploy ZIP az webapp deployment source config-zip \ --name myapp-unique-name \ --resource-group myapp-rg \ --src app.zip
Option C: Git Deployment
bash# Get Git deployment URL az webapp deployment source config-local-git \ --name myapp-unique-name \ --resource-group myapp-rg # Add as remote git remote add azure <deployment-url> # Push to deploy git push azure main
Option D: GitHub Actions
- Go to Deployment Center in Azure Portal
- Select GitHub as source
- Choose repository and branch
- Azure generates workflow file
yaml# .github/workflows/azure.yml (auto-generated) name: Deploy to Azure Web App on: push: branches: [main] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: '20' - name: Install and build run: | npm ci npm run build - name: Deploy to Azure uses: azure/webapps-deploy@v2 with: app-name: myapp-unique-name publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }} package: .
Step 4: Configure Application Settings
Environment Variables
bash# Set environment variable az webapp config appsettings set \ --name myapp-unique-name \ --resource-group myapp-rg \ --settings NODE_ENV=production API_KEY=your-key # View settings az webapp config appsettings list \ --name myapp-unique-name \ --resource-group myapp-rg \ --output table # Delete setting az webapp config appsettings delete \ --name myapp-unique-name \ --resource-group myapp-rg \ --setting-names API_KEY
Connection Strings
bash# Set connection string az webapp config connection-string set \ --name myapp-unique-name \ --resource-group myapp-rg \ --connection-string-type SQLAzure \ --settings DefaultConnection="Server=..."
Application Configuration
bash# Set startup command az webapp config set \ --name myapp-unique-name \ --resource-group myapp-rg \ --startup-file "npm start" # Set Node.js version az webapp config set \ --name myapp-unique-name \ --resource-group myapp-rg \ --linux-fx-version "NODE|20-lts" # Enable WebSockets az webapp config set \ --name myapp-unique-name \ --resource-group myapp-rg \ --web-sockets-enabled true # Always On (keeps app warm) az webapp config set \ --name myapp-unique-name \ --resource-group myapp-rg \ --always-on true
Step 5: View Logs
Stream Logs
bash# Enable logging az webapp log config \ --name myapp-unique-name \ --resource-group myapp-rg \ --application-logging filesystem \ --detailed-error-messages true \ --failed-request-tracing true \ --web-server-logging filesystem # Stream live logs az webapp log tail \ --name myapp-unique-name \ --resource-group myapp-rg
Download Logs
bash# Download log files az webapp log download \ --name myapp-unique-name \ --resource-group myapp-rg \ --log-file webapp_logs.zip
Deployment Slots (Standard tier+)
Test changes before going live:
bash# Create staging slot az webapp deployment slot create \ --name myapp-unique-name \ --resource-group myapp-rg \ --slot staging # Deploy to staging az webapp deployment source config-zip \ --name myapp-unique-name \ --resource-group myapp-rg \ --slot staging \ --src app.zip # Swap staging to production az webapp deployment slot swap \ --name myapp-unique-name \ --resource-group myapp-rg \ --slot staging \ --target-slot production
Slot Settings
Settings that stay with the slot (not swapped):
bash# Mark setting as slot-specific az webapp config appsettings set \ --name myapp-unique-name \ --resource-group myapp-rg \ --slot staging \ --slot-settings SLOT_NAME=staging
Auto-Scaling (Standard tier+)
bash# Enable autoscale az monitor autoscale create \ --resource-group myapp-rg \ --name myapp-autoscale \ --resource /subscriptions/.../resourceGroups/myapp-rg/providers/Microsoft.Web/serverfarms/myapp-plan \ --min-count 1 \ --max-count 5 \ --count 1 # Add CPU-based rule az monitor autoscale rule create \ --resource-group myapp-rg \ --autoscale-name myapp-autoscale \ --condition "Percentage CPU > 70 avg 5m" \ --scale out 1 az monitor autoscale rule create \ --resource-group myapp-rg \ --autoscale-name myapp-autoscale \ --condition "Percentage CPU < 30 avg 5m" \ --scale in 1
Custom Domains
Add Custom Domain
bash# Add custom domain az webapp config hostname add \ --webapp-name myapp-unique-name \ --resource-group myapp-rg \ --hostname www.example.com
Configure DNS
At your DNS provider, add:
| Type | Name | Value |
|---|---|---|
| CNAME | www | myapp-unique-name.azurewebsites.net |
| TXT | asuid.www | (verification ID from Azure) |
For apex domain:
| Type | Name | Value |
|---|---|---|
| A | @ | App Service IP |
| TXT | asuid | (verification ID) |
Free Managed SSL
bash# Create managed certificate az webapp config ssl create \ --name myapp-unique-name \ --resource-group myapp-rg \ --hostname www.example.com # Bind certificate az webapp config ssl bind \ --name myapp-unique-name \ --resource-group myapp-rg \ --certificate-thumbprint THUMBPRINT \ --ssl-type SNI # Enforce HTTPS az webapp update \ --name myapp-unique-name \ --resource-group myapp-rg \ --https-only true
Container Deployment
Deploy Docker containers to App Service:
bash# Create web app for containers az webapp create \ --name myapp-container \ --resource-group myapp-rg \ --plan myapp-plan \ --deployment-container-image-name nginx # Use Azure Container Registry az webapp config container set \ --name myapp-container \ --resource-group myapp-rg \ --container-image-name myregistry.azurecr.io/myapp:latest \ --container-registry-url https://myregistry.azurecr.io \ --container-registry-user myregistry \ --container-registry-password PASSWORD
Enable Continuous Deployment
bash# Enable CD webhook az webapp deployment container config \ --name myapp-container \ --resource-group myapp-rg \ --enable-cd true
Backup and Restore (Standard tier+)
bash# Create storage for backups az storage account create \ --name myappbackups \ --resource-group myapp-rg \ --sku Standard_LRS # Create container az storage container create \ --name backups \ --account-name myappbackups # Get SAS URL EXPIRY=$(date -d "+1 year" +%Y-%m-%d) SAS_URL=$(az storage container generate-sas \ --account-name myappbackups \ --name backups \ --permissions rwdl \ --expiry $EXPIRY \ --output tsv) # Configure backup az webapp config backup create \ --webapp-name myapp-unique-name \ --resource-group myapp-rg \ --container-url "https://myappbackups.blob.core.windows.net/backups?$SAS_URL" \ --frequency 1d \ --retain-one true
VNet Integration (Premium tier)
Connect to private resources:
bash# Create VNet integration az webapp vnet-integration add \ --name myapp-unique-name \ --resource-group myapp-rg \ --vnet myapp-vnet \ --subnet app-subnet
Health Checks
bash# Configure health check az webapp config set \ --name myapp-unique-name \ --resource-group myapp-rg \ --generic-configurations '{"healthCheckPath": "/api/health"}'
Troubleshooting
Application Won't Start
bash# Check logs az webapp log tail --name myapp-unique-name --resource-group myapp-rg # SSH into container (Linux) az webapp ssh --name myapp-unique-name --resource-group myapp-rg # Check if port is correct # App must listen on PORT environment variable
Slow Performance
bash# Check metrics az monitor metrics list \ --resource /subscriptions/.../resourceGroups/myapp-rg/providers/Microsoft.Web/sites/myapp-unique-name \ --metric "CpuPercentage,MemoryPercentage" \ --output table # Scale up az appservice plan update \ --name myapp-plan \ --resource-group myapp-rg \ --sku S1
502 Bad Gateway
- Check application logs for errors
- Verify startup command is correct
- Check health check endpoint works
- Increase startup time:
bashaz webapp config appsettings set \ --name myapp-unique-name \ --resource-group myapp-rg \ --settings WEBSITES_CONTAINER_START_TIME_LIMIT=1800
Node.js Configuration
json// package.json { "scripts": { "start": "node server.js" }, "engines": { "node": "20.x" } }
javascript// server.js - Use PORT env variable const port = process.env.PORT || 3000; app.listen(port, () => { console.log(`Server running on port ${port}`); });
Cost Optimization
- Use Free/Shared for dev - F1 and D1 tiers
- Scale down when not needed - Stop apps at night
- Use deployment slots wisely - Only on Standard+
- Reserved instances - Up to 55% savings for 1-3 years
- Delete unused resources - Plans, slots, backups
bash# Stop app (saves compute costs) az webapp stop --name myapp-unique-name --resource-group myapp-rg # Start app az webapp start --name myapp-unique-name --resource-group myapp-rg # Delete app az webapp delete --name myapp-unique-name --resource-group myapp-rg
Note
Checkpoint: Deploy your application to App Service, configure environment variables, and verify it's accessible at the azurewebsites.net URL.