Async/Await not running in Azure App Service
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
When async and await appear to stop working after deployment to Azure App Service, the platform is usually exposing existing blocking patterns in your code. The issue often presents as slow requests, hanging endpoints, or apparent synchronous behavior under load. A solid fix requires checking call chains, thread usage, and runtime diagnostics rather than only changing one controller method.
Why It Works Locally But Fails In App Service
Local testing often has low concurrency and fast dependencies, so blocking calls are less visible. In App Service, request concurrency is higher and thread pool starvation appears quickly if any request path uses blocking waits.
Common patterns that cause this:
- calling
.Resultor.Wait()on tasks, - wrapping I O code inside
Task.Runinstead of using native async APIs, - long synchronous startup work that delays request threads,
- hidden blocking inside third party SDK calls.
In other words, await can be present in source code while important parts of execution are still effectively synchronous.
Start With A Minimal Correct Pattern
A request path should stay asynchronous from controller to network or database layer.
This path has no sync wait and no unnecessary thread hopping.
Detect Sync Over Async Quickly
A practical diagnostic pass usually finds problems fast.
- Search for
.Result,.Wait(), and.GetAwaiter().GetResult(). - Confirm that data access library calls are truly async methods.
- Add timing logs around each awaited external call.
- Capture thread pool counters during load tests.
Use App Service application logs and distributed tracing to compare request timeline locally versus cloud. If one awaited call consistently blocks for long intervals, inspect that dependency first.
App Service Deployment Considerations
Async bugs are often amplified by deployment configuration.
- Set realistic request timeout and outbound dependency timeout values.
- Ensure connection pooling is configured for database and HTTP clients.
- Avoid creating
HttpClientper request. - Keep background loops out of request code paths.
- Scale up or out only after fixing blocking code.
Scaling without removing blocking calls usually increases cost before improving reliability.
Background Work Should Not Block Requests
If you need long running operations, queue the work and return quickly from the API.
This separates user latency from background processing and prevents request thread exhaustion.
About ConfigureAwait
In ASP.NET Core, there is no legacy synchronization context in the same way as classic ASP.NET. That means ConfigureAwait(false) is usually not required to avoid deadlocks, though it can still be used for library code conventions. The bigger risk is still blocking waits, not missing ConfigureAwait.
Common Pitfalls
- Assuming Azure changed async semantics when code contains sync waits.
- Using
Task.Runaround I O and expecting better scalability. - Creating many short lived
HttpClientinstances and exhausting sockets. - Ignoring cancellation tokens in long network calls.
- Treating scale out as a replacement for async correctness.
Summary
- App Service usually reveals existing sync over async issues under higher concurrency.
- Remove blocking waits such as
.Resultand.Wait()from request paths. - Keep controller to dependency call chains fully asynchronous.
- Use logs and thread pool metrics to pinpoint blocking boundaries.
- Separate long running work from request handling for predictable latency.

