Home / Azure

AzureHow-toIntermediate

Optimization: App Service plan consolidation, density and autoscale that follows real load

App Service estates grow one plan per app, each running a fixed instance count all day. Map apps to plans, measure CPU and memory, consolidate compatible apps, move to current SKUs, and replace fixed instance counts with rule-based or automatic scaling.

Deepak Bhardwaj3 min read

Applies toAzure App Service (Windows and Linux) on Basic, Standard, Premium v3/v4 plans; Azure Monitor autoscale

What you usually find

  • 40 plans, many at 5–10% CPU, each with 2–3 instances.
  • Old SKUs (Standard, Premium v2) when newer generations give more for the money.
  • Instance counts set during a launch and never changed.

You pay per plan instance, not per app, so this is where the savings are.

1. Inventory

az appservice plan list --query "[].{plan:name, rg:resourceGroup, sku:sku.name, workers:sku.capacity, apps:numberOfSites, os:kind}" -o table
// Azure Resource Graph: apps per plan
resources
| where type == "microsoft.web/sites"
| extend plan = tostring(split(properties.serverFarmId, "/")[-1])
| summarize apps = make_list(name) by plan, resourceGroup

2. Measure per plan

From plan metrics over 30 days: CpuPercentage and MemoryPercentage (p95 and max), plus per-app Requests, Http5xx, AverageResponseTime.

Plan profile Action
p95 CPU < 20%, memory < 50% Fewer instances, smaller SKU, or merge with another plan
Memory high, CPU low Memory-optimised SKU (P*mv3) or fewer apps per plan
Spiky CPU Autoscale instead of a high fixed count
Consistently busy Leave it; maybe a newer SKU

3. Consolidate compatible apps

Apps can share a plan when they have the same OS, region and resource group/webspace, similar compliance needs, and no "noisy neighbour" risk to each other. Typical merges: several small internal apps; an app with its admin portal.

Move an app to another plan (same resource group and region):

az webapp update -g rg-web -n intranet-api --set serverFarmId=/subscriptions/<sub>/resourceGroups/rg-web/providers/Microsoft.Web/serverfarms/plan-shared-p1v3

Don't merge production with dev/test, and keep critical customer-facing apps on their own plans.

4. Per-app scaling for dense plans

On a shared plan, cap busy apps so they don't use every instance:

az appservice plan update -g rg-web -n plan-shared-p1v3 --per-site-scaling true
az webapp update -g rg-web -n intranet-api --set siteConfig.numberOfWorkers=2

5. Move to current SKUs

Changing SKU within the same family is an in-place scale. Newer Premium generations often give better price per core and memory. Test in a slot or non-prod first; a move to a different deployment unit may change outbound IPs, so check firewall allow-lists that use them.

az appservice plan update -g rg-web -n plan-orders --sku P1v3

6. Autoscale

Rule-based (Standard and up):

az monitor autoscale create -g rg-web --resource plan-orders --resource-type Microsoft.Web/serverfarms \
  --name as-plan-orders --min-count 2 --max-count 8 --count 2
az monitor autoscale rule create -g rg-web --autoscale-name as-plan-orders \
  --condition "CpuPercentage > 70 avg 10m" --scale out 2
az monitor autoscale rule create -g rg-web --autoscale-name as-plan-orders \
  --condition "CpuPercentage < 35 avg 15m" --scale in 1

Keep a gap between scale-out and scale-in thresholds to avoid flapping, and add a schedule profile if load is predictable (office hours).

Automatic scaling (Premium v2/v3 and newer): the platform scales on HTTP load, with pre-warmed instances:

az appservice plan update -g rg-web -n plan-orders --elastic-scale true --max-elastic-worker-count 10
az webapp update -g rg-web -n orders-api --minimum-elastic-instance-count 2 --prewarmed-instance-count 1

7. Keep at least two instances in production

Two instances (ideally zone-redundant plans) keep the app up during platform maintenance and instance restarts. Savings shouldn't come from going down to one.

Tip

Turn on Health check on every app before tuning autoscale. Without it, unhealthy instances stay in rotation and scaling decisions are based on a broken instance's metrics.

Checklist

  • Plans, apps and SKUs inventoried
  • 30-day CPU/memory p95 per plan
  • Compatible apps consolidated; prod separated from non-prod
  • Per-app scaling on dense plans
  • Current SKUs; outbound IP changes checked
  • Autoscale or automatic scaling with ≥ 2 instances in prod