Skip to main content
Technology & EngineeringAzure Services374 lines

App Service

Azure App Service for hosting web apps, REST APIs, and mobile backends

Quick Summary34 lines
You are an expert in Azure App Service for hosting production web applications, REST APIs, and mobile backends on a fully managed platform.

## Key Points

- **Ignoring SIGTERM for graceful shutdown** -- When scaling in or swapping slots, App Service sends SIGTERM. Applications that do not handle it drop in-flight requests, causing errors for users.
- **App Service Plan**: The compute resources (VM instances) that host your apps. Defines SKU, region, and scale.
- **Web App**: The application running on the plan. Multiple apps can share a plan.
- **Deployment Slots**: Staging environments that can be swapped with production for zero-downtime deployments.
- **SKU Tiers**: Free, Shared, Basic, Standard, Premium v3, Isolated v2 (for VNET isolation).
1. **Always use deployment slots**: Deploy to a staging slot, validate, then swap to production. This gives zero-downtime deployments and instant rollback.
2. **Enable health checks**: Configure the health check path so App Service can automatically replace unhealthy instances.
3. **Use managed identity**: Access Azure services (Key Vault, SQL, Storage) with managed identity instead of connection strings.
4. **Set Always On**: On Basic tier and above, enable "Always On" to prevent the app from being unloaded after idle periods.
5. **Configure startup commands for Linux apps**:
6. **Use ZIP deploy or Run From Package**: Faster and more reliable than FTP or local Git.
7. **Scale out, not up**: Prefer horizontal scaling (more instances) over vertical scaling (larger instances) for better resilience.

## Quick Example

```bash
az webapp config set \
     --name mywebapp \
     --resource-group myResourceGroup \
     --always-on true
```

```bash
az webapp config set \
     --name mywebapp \
     --resource-group myResourceGroup \
     --startup-file "node dist/server.js"
```
skilldb get azure-services-skills/App ServiceFull skill: 374 lines
Paste into your CLAUDE.md or agent config

Azure App Service — Cloud Services

You are an expert in Azure App Service for hosting production web applications, REST APIs, and mobile backends on a fully managed platform.

Core Philosophy

App Service is a platform, not just hosting. It provides deployment slots, managed certificates, auto-scaling, health checks, and VNET integration out of the box. The value proposition is that you focus on application code while Azure handles OS patching, load balancing, and TLS termination. If you find yourself SSHing into instances, installing system packages, or configuring reverse proxies, you are fighting the platform instead of using it.

Deployment slots are the most important App Service feature that most teams underuse. Every production deployment should go through a staging slot first. Deploy to staging, run smoke tests against the staging URL, then swap to production for zero-downtime deployment. If something goes wrong, swap back immediately. This pattern eliminates "deploy and pray" and gives you instant rollback with no cold start penalty because the staging slot is already warmed up.

Scale horizontally, not vertically. When your application needs more capacity, add more instances (scale out) rather than moving to a larger SKU (scale up). Horizontal scaling provides better fault tolerance -- losing one of ten instances degrades performance by 10%, while losing one large instance takes down the entire application. Configure auto-scale rules based on CPU percentage or request count, and set both scale-out and scale-in rules to avoid flapping.

Anti-Patterns

  • Deploying directly to production without slots -- Every failed deployment becomes a production outage. Use staging slots with swap-based deployments for zero-downtime releases and instant rollback.
  • Using Free or Shared tier for production workloads -- These tiers share infrastructure, have no SLA, and lack critical features like deployment slots, custom domains with SSL, auto-scaling, and Always On.
  • Forgetting to enable Always On -- Without Always On, App Service unloads the application after idle periods. The next request triggers a cold start that can take 10+ seconds. Enable Always On for any user-facing application.
  • Not configuring slot-sticky settings -- Database connection strings and environment-specific flags must be marked as slot settings. Without this, swapping a slot to production also swaps its configuration, pointing production at the staging database.
  • Ignoring SIGTERM for graceful shutdown -- When scaling in or swapping slots, App Service sends SIGTERM. Applications that do not handle it drop in-flight requests, causing errors for users.

Overview

Azure App Service is a fully managed platform-as-a-service (PaaS) for building, deploying, and scaling web applications. It supports .NET, Node.js, Python, Java, PHP, Ruby, and custom containers. App Service handles infrastructure maintenance, security patching, and scaling.

Key concepts:

  • App Service Plan: The compute resources (VM instances) that host your apps. Defines SKU, region, and scale.
  • Web App: The application running on the plan. Multiple apps can share a plan.
  • Deployment Slots: Staging environments that can be swapped with production for zero-downtime deployments.
  • SKU Tiers: Free, Shared, Basic, Standard, Premium v3, Isolated v2 (for VNET isolation).

Setup & Configuration

Create an App Service with Azure CLI

# Create an App Service Plan (Linux, Premium v3)
az appservice plan create \
  --name myAppServicePlan \
  --resource-group myResourceGroup \
  --is-linux \
  --sku P1V3 \
  --location eastus

# Create a Node.js web app
az webapp create \
  --name mywebapp \
  --resource-group myResourceGroup \
  --plan myAppServicePlan \
  --runtime "NODE:18-lts"

# Create a Python web app
az webapp create \
  --name mypythonapp \
  --resource-group myResourceGroup \
  --plan myAppServicePlan \
  --runtime "PYTHON:3.11"

# Create a .NET web app
az webapp create \
  --name mydotnetapp \
  --resource-group myResourceGroup \
  --plan myAppServicePlan \
  --runtime "DOTNETCORE:8.0"

# Configure app settings (environment variables)
az webapp config appsettings set \
  --name mywebapp \
  --resource-group myResourceGroup \
  --settings \
    NODE_ENV=production \
    DATABASE_URL="@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/DbUrl/)"

# Configure connection strings
az webapp config connection-string set \
  --name mywebapp \
  --resource-group myResourceGroup \
  --connection-string-type SQLAzure \
  --settings DefaultConnection="Server=tcp:myserver.database.windows.net..."

# Enable system-assigned managed identity
az webapp identity assign \
  --name mywebapp \
  --resource-group myResourceGroup

Configure custom domain and TLS

# Map a custom domain
az webapp config hostname add \
  --webapp-name mywebapp \
  --resource-group myResourceGroup \
  --hostname www.example.com

# Create a managed certificate (free)
az webapp config ssl create \
  --name mywebapp \
  --resource-group myResourceGroup \
  --hostname www.example.com

# Bind the certificate
az webapp config ssl bind \
  --name mywebapp \
  --resource-group myResourceGroup \
  --certificate-thumbprint <thumbprint> \
  --ssl-type SNI

# Enforce HTTPS
az webapp update \
  --name mywebapp \
  --resource-group myResourceGroup \
  --https-only true

# Set minimum TLS version
az webapp config set \
  --name mywebapp \
  --resource-group myResourceGroup \
  --min-tls-version 1.2

Core Patterns

Deployment with GitHub Actions

# .github/workflows/deploy.yml
name: Deploy to Azure App Service

on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 18

      - run: npm ci
      - run: npm run build
      - run: npm test

      - uses: azure/login@v2
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

      - uses: azure/webapps-deploy@v3
        with:
          app-name: mywebapp
          slot-name: staging
          package: .

      # Run smoke tests against staging
      - run: curl -f https://mywebapp-staging.azurewebsites.net/health

      # Swap staging to production
      - run: |
          az webapp deployment slot swap \
            --name mywebapp \
            --resource-group myResourceGroup \
            --slot staging \
            --target-slot production

Deployment slots for blue-green deploys

# Create a staging slot
az webapp deployment slot create \
  --name mywebapp \
  --resource-group myResourceGroup \
  --slot staging

# Configure slot-specific settings (sticky to the slot)
az webapp config appsettings set \
  --name mywebapp \
  --resource-group myResourceGroup \
  --slot staging \
  --slot-settings \
    SLOT_NAME=staging

# Deploy to staging
az webapp deploy \
  --name mywebapp \
  --resource-group myResourceGroup \
  --slot staging \
  --src-path ./app.zip \
  --type zip

# Swap staging to production (zero-downtime)
az webapp deployment slot swap \
  --name mywebapp \
  --resource-group myResourceGroup \
  --slot staging \
  --target-slot production

# Swap back if issues arise
az webapp deployment slot swap \
  --name mywebapp \
  --resource-group myResourceGroup \
  --slot production \
  --target-slot staging

Auto-scaling configuration

# Enable autoscale on the App Service Plan
az monitor autoscale create \
  --resource-group myResourceGroup \
  --resource myAppServicePlan \
  --resource-type Microsoft.Web/serverFarms \
  --name myAutoscaleSettings \
  --min-count 2 \
  --max-count 10 \
  --count 2

# Add a scale-out rule (CPU > 70% for 10 minutes)
az monitor autoscale rule create \
  --resource-group myResourceGroup \
  --autoscale-name myAutoscaleSettings \
  --condition "CpuPercentage > 70 avg 10m" \
  --scale out 2

# Add a scale-in rule (CPU < 30% for 10 minutes)
az monitor autoscale rule create \
  --resource-group myResourceGroup \
  --autoscale-name myAutoscaleSettings \
  --condition "CpuPercentage < 30 avg 10m" \
  --scale in 1

Health check and diagnostics

# Enable health check endpoint
az webapp config set \
  --name mywebapp \
  --resource-group myResourceGroup \
  --generic-configurations '{"healthCheckPath": "/health"}'

# Enable Application Insights
az monitor app-insights component create \
  --app mywebapp-insights \
  --location eastus \
  --resource-group myResourceGroup

az webapp config appsettings set \
  --name mywebapp \
  --resource-group myResourceGroup \
  --settings \
    APPLICATIONINSIGHTS_CONNECTION_STRING="InstrumentationKey=..."
    ApplicationInsightsAgent_EXTENSION_VERSION="~3"

# Stream live logs
az webapp log tail \
  --name mywebapp \
  --resource-group myResourceGroup

# Enable detailed logging
az webapp log config \
  --name mywebapp \
  --resource-group myResourceGroup \
  --application-logging filesystem \
  --level information \
  --web-server-logging filesystem

VNET Integration and private endpoints

# Integrate the app with a VNET (outbound traffic)
az webapp vnet-integration add \
  --name mywebapp \
  --resource-group myResourceGroup \
  --vnet myVnet \
  --subnet app-subnet

# Route all traffic through VNET
az webapp config appsettings set \
  --name mywebapp \
  --resource-group myResourceGroup \
  --settings WEBSITE_VNET_ROUTE_ALL=1

# Create a private endpoint (inbound traffic)
az network private-endpoint create \
  --name mywebapp-pe \
  --resource-group myResourceGroup \
  --vnet-name myVnet \
  --subnet pe-subnet \
  --private-connection-resource-id $(az webapp show --name mywebapp --resource-group myResourceGroup --query id -o tsv) \
  --group-id sites \
  --connection-name mywebapp-connection

Best Practices

  1. Always use deployment slots: Deploy to a staging slot, validate, then swap to production. This gives zero-downtime deployments and instant rollback.

  2. Enable health checks: Configure the health check path so App Service can automatically replace unhealthy instances.

  3. Use managed identity: Access Azure services (Key Vault, SQL, Storage) with managed identity instead of connection strings.

  4. Set Always On: On Basic tier and above, enable "Always On" to prevent the app from being unloaded after idle periods.

    az webapp config set \
      --name mywebapp \
      --resource-group myResourceGroup \
      --always-on true
    
  5. Configure startup commands for Linux apps:

    az webapp config set \
      --name mywebapp \
      --resource-group myResourceGroup \
      --startup-file "node dist/server.js"
    
  6. Use ZIP deploy or Run From Package: Faster and more reliable than FTP or local Git.

    az webapp deploy \
      --name mywebapp \
      --resource-group myResourceGroup \
      --src-path ./deploy.zip \
      --type zip
    
  7. Scale out, not up: Prefer horizontal scaling (more instances) over vertical scaling (larger instances) for better resilience.

  8. Enable diagnostic logging and Application Insights: Captures request traces, exceptions, dependencies, and custom telemetry.

Common Pitfalls

  • Using Free/Shared tier for production: These tiers share infrastructure, have no SLA, and lack features like deployment slots, custom domains with SSL, and autoscaling.

  • Not configuring slot-sticky settings: Database connection strings and feature flags that differ between staging and production must be marked as "slot settings" to avoid swapping them.

  • Cold start after swap: Pre-warm the staging slot before swapping. App Service sends synthetic requests to the app root, but if your app needs specific warm-up paths, add them in web.config or use the health check endpoint.

  • Exceeding the plan's memory/CPU: All apps on a plan share resources. An overloaded plan degrades all apps. Monitor per-app metrics.

  • Hardcoded file paths: On Linux App Service, the app runs in a container. Use /home for persistent storage, not /tmp.

  • Missing CORS configuration: For SPAs calling an API on App Service, configure CORS in the portal or code. App Service has built-in CORS support that runs before your app code.

    az webapp cors add \
      --name mywebapp \
      --resource-group myResourceGroup \
      --allowed-origins "https://myfrontend.com"
    
  • Not handling graceful shutdown: When swapping or scaling in, App Service sends SIGTERM. Handle it to complete in-flight requests.

    process.on("SIGTERM", () => {
      console.log("SIGTERM received, shutting down gracefully...");
      server.close(() => process.exit(0));
    });
    

Install this skill directly: skilldb add azure-services-skills

Get CLI access →