How to Fix: Azure App Service 503 Service Unavailable

Azure503Critical severity

The Scenario

You’re running a .NET 6 app on a Standard tier Azure App Service, handling a routine deploy via GitHub Actions. Suddenly, the staging slot starts returning 503s, and your production instance follows 10 minutes later after a swap. The app was working fine 2 hours ago, and no code changes touched the networking layer.

Symptoms

Root Cause

The App Service plan has hit its resource limits (CPU, memory, or concurrent requests) due to a traffic spike, a memory leak from a recent deploy, or the autoscale configuration not scaling out fast enough. Alternatively, the application pool or worker process crashed (e.g., from an unhandled exception in Application_Start), and the platform’s health check is failing to restart it cleanly.

Resolution (Step-by-Step)

  1. Check the App Service Plan metrics to confirm resource exhaustion:

    az monitor metrics list --resource <app-service-plan-id> --metric "CpuPercentage" "MemoryPercentage" --interval PT1H

    Look for sustained >90% CPU or memory.

  2. Scale out the App Service Plan immediately to handle load:

    az appservice plan update --name <plan> --resource-group <rg> --number-of-workers 3
  3. Restart the App Service to force a clean worker process:

    az webapp restart --name <app> --resource-group <rg>
  4. Check the application event log for crashes:

    az webapp log download --name <app> --resource-group <rg> --log-file app_logs.zip

    Unzip and look for docker.log or eventlog.xml for AppDomain unloaded or OutOfMemoryException.

  5. Disable autoscale temporarily if it’s causing flapping:

    az monitor autoscale delete --name <autoscale-name> --resource-group <rg>
  6. Enable Always On if not already set (prevents idle worker recycling):

    az webapp config set --name <app> --resource-group <rg> --always-on true

Why This Sometimes Doesn’t Work

If the 503 persists after scaling and restart, the issue may be a stuck deployment slot swap or a broken health check endpoint. The swap operation can leave the old worker process in a draining state, rejecting new requests. Check the WEBSITE_SWAP_PROBE_SUCCESS environment variable in the Kudu console (https://<app>.scm.azurewebsites.net). If it’s false, manually trigger a slot swap back to the previous version with az webapp deployment slot swap --name <app> --resource-group <rg> --slot staging --target-slot production. Also verify your /health endpoint returns 200 and is not blocked by IP restrictions.

Verification

Hit the app endpoint and confirm a 200 response:

curl -I https://<app>.azurewebsites.net

Expected output:

HTTP/2 200 OK

Then check the App Service Plan metrics are below 70% CPU/memory:

az monitor metrics list --resource <app-service-plan-id> --metric "CpuPercentage" --interval PT5M --query "value[0].timeseries[0].data[-1].average"

Common Follow-up Questions

Q: What if the 503 only happens during deployments? A: Use deployment slots with auto-swap and pre-warming. Ensure the applicationInitialization config in web.config points to a lightweight warmup path.

Q: Can a VNet integration cause 503s? A: Yes, if the VNet is misconfigured or the target resource (e.g., a database) is unreachable. Check WEBSITE_VNET_ROUTE_ALL=1 and test connectivity from the Kudu console with tcpping <db-host>:1433.

Q: How do I differentiate between a platform 503 and an app 503? A: Check the response body. App-level 503s usually include a stack trace or custom error page. Platform 503s return a generic “Service Unavailable” page from IIS or Azure Front Door.

Prevent This in the Future

Proactive monitoring and alerting on App Service Plan metrics (CPU, memory, HTTP 5xx count) would catch resource exhaustion before it triggers a 503 outage. Teams commonly use Better Stack to aggregate these metrics, set up intelligent alerting thresholds, and correlate incidents across deploys.

Set up uptime monitoring and alerts with Better Stack