App Service 502.5: Process Failure on Startup
Fix App Service 502.5 by reading startup logs first: enable App Service logs, check stdout output, and align the runtime version..
20+ years shipping production backend systems. Drawn from code that ran under real load.
- ✓An Azure App Service you can restart and configure
- ✓Azure CLI installed with az login completed
- ✓Basic familiarity with deploying a web app
- HTTP 502.5 means your app crashed before answering a single request: the platform is fine, your process died on startup
- Turn on App Service logs, restart once, and watch Log Stream: stdout usually names the exact crash within seconds
- The top cause is a runtime mismatch: your app targets .NET 8 while the App Service still runs .NET 6
- Check app settings too: a missing connection string throws at startup and looks just like a code bug
Think of App Service as a restaurant kitchen and your app as the recipe on the wall. Error 502.5 means the kitchen is open but the cook quit before making a single dish. The fix is never to rebuild the kitchen — it's to learn why the cook quit: a missing ingredient (app setting), a wrong oven (runtime mismatch), or a bad first step (startup crash). The kitchen notebook (stdout logs) holds the reason.
You deploy on a quiet Thursday afternoon, open the site to verify, and meet a bare error page: HTTP Error 502.5, process failure. No stack trace, no friendly message, just a number. Your first instinct says Azure is broken. Your second instinct says roll back. Both instincts skip the step that actually ends the outage: reading the one log line that names the crash.
Error 502.5 is App Service telling you the platform did its job and your app died anyway. The front end accepted the request, tried to hand it to your process, and found nothing alive on the other side. The cause is almost always local to your deployment: a runtime the host doesn't have, an exception in startup code, or a setting that exists on your laptop and nowhere else.
Beginners lose the most time here because the error page offers no clues while the clues sit one click away in Log Stream. Veterans lose time too, by redeploying on theories instead of reproducing with logging switched on.
This guide shows the exact order that ends 502.5 fast: enable App Service logs, restart once, read stdout, and fix what it names. You'll learn the runtime mismatch pattern behind most cases, see a real Friday-deploy incident, and leave with a pipeline that refuses to ship a worker that can't start.
What HTTP 502.5 Means on App Service (and What It Doesn't)
HTTP Error 502.5 on App Service has one specific meaning: the front end received your request but the worker process that should answer it failed. On Windows this surfaces through ANCM, the IIS module that launches your dotnet process and proxies traffic to it. On Linux it surfaces through the container's startup command and the language worker. Either way, the platform side is healthy — load balancer, networking, and the web server all work. What failed is your application starting up and staying alive.
This distinction decides your whole response. A platform problem calls for Azure status pages and support tickets. A 502.5 calls for your own logs, because the answer lives in your process's stdout output. Treating it as an outage wastes the first hour; treating it as a startup crash usually ends the incident in fifteen minutes. Check the App Service state first: if it says Running yet every request 502.5s, that's the classic signature — the host is up, the app is down.
The error page itself tells you almost nothing by design, since detailed errors are off by default in production. Don't stare at it. Move immediately to Log Stream with application logging enabled. Beginners often assume the lack of detail means the lack of evidence. The opposite is true: App Service captures stdout, Docker logs, detailed errors, and failed-request traces, but only if you switch them on. The rest of this guide walks that path in the order that wastes the least time.
Reading Stdout Logs: Your Fastest Path to the Crash
Stdout is where dying .NET, Node, and Python apps confess, and App Service captures it once you ask. Application Logging (Filesystem) writes your console output to files under /LogFiles that you can tail live. Detailed Error Messages add the rich HTML error pages with the failing module and handler. Failed Request Tracing records the full IIS pipeline for requests that match your failure definitions. Together they turn a bare 502.5 into a named exception with a stack trace.
The workflow is deliberately boring: enable the three log sources, restart the app once, and tail the stream. The restart matters because startup crashes only emit their evidence during startup — tailing a long-dead process shows nothing. Watch the first thirty seconds after the restart line. The first exception in that window is almost always the cause; the errors after it are consequences of the process dying, not separate problems worth chasing.
For Windows apps, supplement Log Stream with the Kudu file browser at your-app.scm.azurewebsites.net, where eventlog.xml and the per-process stdout files persist across restarts. For Linux, the Docker logs in the same LogFiles directory show container startup, package restores, and the exact command that failed. The snippet below enables everything in one pass so you never debug blind again.
Runtime and Version Mismatches That Kill Startup
Most 502.5s come down to a version the host doesn't have. A framework-dependent .NET app needs the exact shared runtime installed on the worker: .NET 8 code on a .NET 6 worker dies instantly with a framework-not-found message. The same applies to Node and Python stacks — package.json asking for Node 20 on a Node 18 worker, or requirements needing a Python the image lacks. The deploy succeeds because deployment never runs your code; startup fails because startup always does.
Confirm both sides before changing anything. The platform side comes from az webapp config show, which reports linuxFxVersion or the Windows stack. The available side comes from az webapp list-runtimes, which lists every runtime your region actually offers — regions differ, so never assume. The app side comes from your project file's TargetFramework or engines field. When the major versions disagree, you've found the crash without reading another log line.
You have two durable fixes. The quick one sets the App Service stack to match the app. The stronger one publishes self-contained, bundling the runtime with your deployment so host versions stop mattering entirely. Self-contained costs some artifact size and gives up automatic runtime patching, but it deletes an entire class of midnight pages. Either way, add a pipeline assertion comparing TargetFramework to the stack — framework bumps should be impossible to ship without the hosting change.
App Settings, Connection Strings, and Startup Throws
After runtimes, missing configuration is the biggest startup killer. Modern apps read connection strings, Key Vault URIs, and feature flags during startup, and a single absent key throws before the first request binds. It works on every laptop because local settings files and user secrets fill the gaps; it dies in Azure because the publish step deliberately excludes those files. The stdout log names the missing key plainly, which makes this a five-minute fix once you look.
Audit the effective settings rather than trusting memory. The portal's Configuration blade and the appsettings list command show exactly what the worker sees, including slot-specific overrides and Key Vault references. Diff that list against your local configuration on every incident — the missing entry usually jumps out immediately. Pay special attention after slot swaps, because a setting marked slot-specific stays with the slot while everything else moves, silently stranding the swapped app without its database string.
The structural fix has two halves. First, keep secrets out of files entirely: store them as app settings backed by Key Vault references so every environment resolves the same keys. Second, validate required settings at startup and crash loudly with the key name when one is absent. A startup error that says missing required setting: OrdersDbConnection beats a NullReference ten stack frames deep, both for your debugging and for the next person on call.
Diagnosing With Log Stream, Kudu, and Failed Request Tracing
When Log Stream is thin, go one layer deeper. Kudu — the .scm site next to every App Service — exposes the worker's filesystem, process explorer, and debug console through both a browser UI and a REST API. Under /LogFiles you'll find the Docker logs on Linux, detailed error HTML pages, failed-request traces, and on Windows the eventlog.xml plus per-process stdout files. These persist across restarts, so they hold evidence that live tailing missed.
Authenticate with the app's publishing credentials, which you can fetch from the CLI without resetting anything. Then curl the LogFiles virtual filesystem directly, or download the whole directory as a zip for offline grepping. The failed-request tracing XML files are verbose, but the winning move is simple: search for the first 502 status after your restart timestamp and read the module that raised it. If ANCM raised it before your code ran, suspect web.config or the startup command; if your code ran and threw, the exception details sit a few lines above.
Don't overlook the process explorer in Kudu either. If your process appears and vanishes in a loop, that's the platform restarting a crashing worker — consistent with a startup throw. If no process appears at all, the launch itself failed, which points at configuration rather than code. Either observation narrows the search before you read a single stack trace.
Hardening Deploys So a Bad Startup Can't Take You Down
The final step is making a startup crash undeployable. Deployment slots exist precisely for this: deploy to staging, warm it with real requests, and swap only when the health endpoint answers. Auto-swap can do this hands-free, but only after you've proven the warmup path exercises true startup — a health check that returns 200 without touching the database proves nothing about the database string. Wire the warmup to the same readiness probe your orchestrator would use, hitting dependencies and failing loudly.
Keep the launch configuration in version control next to the code. On Windows that means web.config with the ANCM handler settings and process path; on Linux it means the explicit startup command in Configuration. Review changes to these files like code, because they are code — a one-line edit there carries the same blast radius as a migration. Diff them in the pipeline and block deploys when they change unexpectedly.
Close the loop with staged verification: deploy, call the health endpoint from the pipeline, run the smoke suite, then swap. If any step fails, the pipeline stops and staging holds the broken build where it can't hurt anyone. Rollback becomes a re-swap, measured in seconds. Teams that run this loop stop fearing deploys, because the pipeline has already survived every startup crash they're capable of shipping.
The Framework Upgrade That Never Reached the Hosting Stack
az webapp config show and saw the stack still pinned to .NET 6 while the project targeted .NET 8 — the latest deploy had silently carried the new target framework. They updated the stack to .NET 8 with az webapp config set, enabled filesystem logging, and restarted once. The site recovered immediately. The lasting fix was publishing self-contained for that service plus a pipeline check that fails the build when TargetFramework and the App Service stack disagree.- Turn logging on before you restart anything. Every blind restart destroys evidence; one logged restart usually names the crash and ends the incident.
- Framework upgrades must include the hosting stack. A TargetFramework bump without the matching App Service stack is a guaranteed 502.5 waiting for the next deploy.
- Alert on 502.x rate, not just on downtime. The error-rate spike pages you while the first users are still retrying, instead of after all of them have failed.
az webapp log config --name <APP> --resource-group <RG> --application-logging filesystem --detailed-error-messages true --failed-request-tracing true --web-server-logging filesystem. Then restart with az webapp restart --name <APP> --resource-group <RG> and watch az webapp log tail --name <APP> --resource-group <RG> — the crash line appears within seconds.az webapp config show --name <APP> --resource-group <RG> --query "{linux:linuxFxVersion, windows:windowsFxVersion, net:netFrameworkVersion}" and compare against your project's TargetFramework. If they disagree on the major version, set the right stack with az webapp config set or republish self-contained.az webapp config appsettings list --name <APP> --resource-group <RG> and diff them against your local configuration. Add the missing key with az webapp config appsettings set --settings KEY=value, then restart once and re-check Log Stream.az webapp deployment list-publishing-credentials, then browse https://<APP>.scm.azurewebsites.net/api/vfs/LogFiles/ (stdout files, eventlog.xml) with curl. On Windows the ANCM startup errors land here even when Log Stream shows little.az webapp log download --name <APP> --resource-group <RG> --log-file /tmp/app-logs.zip, unzip, and grep the stdout and Docker logs for the first exception after the restart timestamp. The first error is the cause; everything after it is fallout.| File | Command / Code | Purpose |
|---|---|---|
| enable-app-logs.sh | az webapp log config --name <APP_NAME> --resource-group <RG> \ | Reading Stdout Logs |
| check-app-runtime.sh | az webapp config show --name <APP_NAME> --resource-group <RG> \ | Runtime and Version Mismatches That Kill Startup |
| fix-app-settings.sh | az webapp config appsettings list --name <APP_NAME> --resource-group <RG> -o tab... | App Settings, Connection Strings, and Startup Throws |
| pull-kudu-logs.sh | az webapp deployment list-publishing-credentials \ | Diagnosing With Log Stream, Kudu, and Failed Request Tracing |
Key takeaways
Common mistakes to avoid
5 patternsDeploying a framework-dependent app without pinning the App Service runtime
az webapp config set --linux-fx-version. Better yet, publish self-contained so the app carries its own runtime and stops depending on the platform's installed SDKs.Guessing at the crash instead of turning on stdout logging
az webapp log config --application-logging filesystem --detailed-error-messages true. Reproduce, read Log Stream, then fix the named exception.Reading secrets from appsettings.json files that never ship
Letting web.config or the Linux startup command drift out of version control
Swapping slots without warming up the staging slot first
Interview Questions on This Topic
What does HTTP error 502.5 mean on Azure App Service?
Frequently Asked Questions
20+ years shipping production backend systems. Drawn from code that ran under real load.
That's Azure. Mark it forged?
6 min read · try the examples if you haven't