Health checks and autoscale: the basics
Why a production App Service running on one instance is one bad request away from an outage
A web app on Azure App Service runs on one or more instances behind a load balancer. By default a new plan runs a single instance, and that single instance is a single point of failure: if the app on it hangs, leaks memory or wedges on a bad deployment, every request goes to the one place that cannot answer them. Azure Advisor surfaces two reliability recommendations that close this gap. The first, 'Enable Health check for App Service', asks you to give App Service a URL path it can ping so it can tell a healthy instance from a sick one. The second is the scale-out guidance that pairs with it: run at least two instances and let an autoscale rule add more when the plan comes under CPU or memory pressure.
Health check works by pinging the path you configure on every instance once a minute. If an instance fails to return a status code between 200 and 299 for the configured number of consecutive pings, App Service marks it unhealthy and the load balancer stops sending it traffic. If it stays unhealthy, App Service replaces it. The default failure threshold is 10 consecutive pings, tunable down to 2 through the WEBSITE_HEALTHCHECK_MAXPINGFAILURES app setting. The catch Advisor calls out explicitly: this only helps if you have somewhere to reroute to. On Free and Shared plans, which cannot scale out, an unhealthy instance is never replaced, so the recommendation is to run Basic tier or higher with two or more instances.
Autoscale is the other half. An autoscale setting on the App Service plan watches a metric, usually CpuPercentage or MemoryPercentage, and adds an instance when the metric crosses a threshold for a sustained window, then removes one when load falls away. The pairing is what makes the app resilient: health check reroutes around the instance that is already broken, and autoscale adds the headroom so a traffic spike never drives every instance into the ground at once. Neither is on by default, and most apps that fall over in production were missing one or both.
In this lesson you will learn how App Service decides an instance is unhealthy and reroutes around it, how autoscale adds capacity under load, and how to turn on both for the apps that warrant them without overspending on the ones that do not. The lesson is grounded in two Azure Advisor reliability recommendations: 'Enable Health check for App Service' and the scale-out guidance that goes with it.
The minute that the load balancer waits
Health check does not yank an instance out the moment one ping fails. It pings every instance once a minute, and an instance has to fail the check for the configured number of consecutive pings before it is pulled from rotation. The default is 10 consecutive failures, which means a flapping instance can keep taking traffic for the better part of ten minutes before the platform gives up on it. That default is deliberately conservative, to avoid evicting an instance over a transient blip, but for a busy production app it is often far too slow. Dropping WEBSITE_HEALTHCHECK_MAXPINGFAILURES to 2 tightens that to roughly two minutes, which is usually the right trade for a customer-facing app: fast enough to matter, slow enough not to evict on a single hiccup.
Finding the production apps with no safety net
Priya runs the platform team at a growing online retailer. Azure Advisor has started flagging 'Enable Health check for App Service' against several web apps, and a recent Saturday outage, one wedged instance that nobody noticed until customers complained, has made the recommendation suddenly concrete.
Rather than enable health check blindly everywhere, Priya starts by listing which apps actually have a health check path set and how many instances each plan runs, so the single-instance production apps, the ones with no safety net at all, can be separated from the ones that are already fine.
List each web app, whether it has a health check path configured, and how many instances its plan runs. An app with no path and one instance is the highest-risk shape.
A revenue-bearing app such as shop-checkout with no health check path is the highest-value target. Confirm its plan runs at least two instances before, or as part of, turning health check on.
How App Service decides an instance is unhealthy and how autoscale respondsdeep dive
Health check is driven by one property on the app, siteConfig.healthCheckPath, and one app setting that tunes it. When healthCheckPath is set, App Service pings that path on every instance once a minute and expects a 2xx status code. The path must be reachable without authentication, because the pinger is anonymous, and it should do real work, checking the database connection and critical dependencies, rather than just returning 200 from a static route, otherwise it reports an instance as healthy while the app behind it is broken. An instance that fails for WEBSITE_HEALTHCHECK_MAXPINGFAILURES consecutive pings, default 10 and tunable between 2 and 10, is removed from the load balancer; if it stays unhealthy it is replaced.
The behaviour that catches people out is the dependency on scale-out. Removing an instance from the load balancer only helps if other instances are there to take the traffic. On a single instance there is nowhere to reroute to, so pulling it from rotation would mean serving nothing, and on Free and Shared plans, which cannot scale out at all, an unhealthy instance is never replaced. This is precisely why Advisor pairs the health check recommendation with scale-out guidance: the feature is only as good as the spare capacity behind it.
Autoscale lives on the App Service plan, not the app, as a Microsoft.Insights autoscale setting. It defines a profile with a minimum, maximum and default instance count, and rules that fire when a metric such as CpuPercentage or MemoryPercentage stays over a threshold for a time window. A scale-out rule adds instances; a scale-in rule removes them once load falls. A cooldown between actions stops the plan oscillating. The strongest production posture combines all three: a health check path that does real work, a failure threshold tuned to your tolerance, and an autoscale rule with a floor of at least two instances so there is always somewhere healthy to send traffic.
What is the impact of running without health checks and autoscale?
The direct impact is availability. A single-instance app with no health check has no mechanism to survive its own failure: when the instance hangs, leaks memory or wedges on a bad deploy, every request hits the one place that cannot answer, and the app is down until a human notices and restarts it. Mean time to recovery becomes however long it takes someone to be paged, diagnose and act, which on a quiet night can be the better part of an hour. With health check and a second instance, the platform reroutes around the sick instance in about a minute and replaces it, and the user-visible outage often never happens.
The second-order impact is the traffic spike that has nowhere to go. An app pinned to a fixed instance count meets a surge, a campaign, a busy Saturday, a viral moment, by driving the instances it has into saturation, where latency climbs and requests start timing out, which is its own outage. Autoscale turns that spike into a few extra instances for a few hours rather than a self-inflicted incident, and gives the capacity back when the spike passes so you are not paying for peak headroom all year.
On the reliability side, both settings are exactly what a resilience review or an Advisor reliability score will look for on a production app. A health check path and an autoscale rule with a floor of two instances are the cheapest, most legible evidence that a customer-facing app can survive a single instance failing or a normal traffic surge without becoming an incident.
How do you make an App Service self-healing safely?
Work the two settings together, in order. The order matters: give the load balancer somewhere to reroute to before you turn health check loose, so enabling it never leaves a single instance serving nothing.
1. Scale out to at least two instances first
Health check only helps if there is a healthy instance to reroute to, so move the plan to Basic tier or higher and run a minimum of two instances. On Free or Shared plans an unhealthy instance is never replaced, so a single-instance app cannot benefit from health check at all. This is the step that carries the recurring cost, so do it on the apps that warrant it.
2. Add a health check path that does real work
Set siteConfig.healthCheckPath to a dedicated route such as /healthz that actually exercises the app's critical dependencies, the database, downstream services, rather than returning a static 200. The path must allow anonymous access so the platform pinger can reach it. A shallow health check that always passes is worse than none, because it hides a broken app behind a green light.
3. Tune the failure threshold to your tolerance
The default WEBSITE_HEALTHCHECK_MAXPINGFAILURES of 10 means an instance keeps taking traffic for nearly ten minutes before it is pulled. For a customer-facing app, drop it to 2 so a wedged instance is evicted in roughly two minutes, fast enough to matter without evicting on a single transient blip.
4. Add an autoscale rule with a floor of two
Put an autoscale setting on the plan with a minimum of two instances, a sensible maximum, and a scale-out rule on CpuPercentage or MemoryPercentage with a scale-in rule and a cooldown so it does not oscillate. This keeps the safety net in place at quiet times and adds capacity for spikes, giving it back when load falls so you are not paying for peak headroom all year.
# 1. Scale the plan to Basic+ and run at least two instances first,
# so health check has somewhere to reroute to.
az appservice plan update \
--name asp-prod --resource-group rg-web \
--sku B2 --number-of-workers 2
# 2. Set a health check path that exercises real dependencies,
# and tune the failure threshold to ~2 minutes.
az webapp config set \
--name shop-checkout --resource-group rg-web \
--generic-configurations '{"healthCheckPath": "/healthz"}'
az webapp config appsettings set \
--name shop-checkout --resource-group rg-web \
--settings WEBSITE_HEALTHCHECK_MAXPINGFAILURES=2
# 3. Add an autoscale rule on the plan with a floor of two instances.
ASP_ID=$(az appservice plan show \
--name asp-prod --resource-group rg-web --query id -o tsv)
az monitor autoscale create \
--resource-group rg-web --name shop-autoscale \
--resource "$ASP_ID" \
--min-count 2 --max-count 10 --count 2
az monitor autoscale rule create \
--resource-group rg-web --autoscale-name shop-autoscale \
--condition "CpuPercentage > 70 avg 10m" --scale out 1 --cooldown 5
az monitor autoscale rule create \
--resource-group rg-web --autoscale-name shop-autoscale \
--condition "CpuPercentage < 30 avg 10m" --scale in 1 --cooldown 10 Quick quiz
Question 1 of 5Azure Advisor recommends 'Enable Health check for App Service' on a production app running a single instance on a Basic plan. What is the correct way to act on it?
You scored
0 / 5
Keep learning
Go deeper on how App Service health check and Azure Monitor autoscale work and how to configure them.
- Monitor App Service instances using Health check How health check pings instances, the failure threshold, and the scale-out dependency.
- Get started with autoscale in Azure How to create an autoscale setting with profiles, scale-out and scale-in rules.
- Reliability recommendations in Azure Advisor The full list of Advisor reliability recommendations, including health check for App Service.
You can now turn a single-instance App Service into a self-healing one: scale out to at least two instances so there is somewhere to reroute to, set a health check path that exercises the app's real dependencies, tune the failure threshold to your tolerance, and add an autoscale rule with a floor of two so the app grows under load instead of buckling. Health check reroutes around the instance that is already broken; autoscale makes sure a spike never breaks them all at once.
Back to the library