Back to CourseIntermediate5 min read
Azure /

Azure Functions: Hosting Plans and Cold Starts

Choose an Azure Functions hosting plan, understand cold starts, and know what Flex Consumption changed.

Bishal Biswas

Bishal Biswas

Self Learner

Published on: October 9, 2026
Last updated: September 7, 2026
Intermediate Topic

The plans

Flex Consumption  pay per use, scales to zero, vnet support, configurable
                  concurrency and always ready instances. The modern default.
Consumption       the original pay per use plan. Limited networking.
Premium           pre warmed instances, no cold start, vnet, unlimited duration
Dedicated         runs on an App Service plan you already pay for
Container Apps    functions hosted alongside other containers

Flex Consumption is where new work should start. It fixed the two things that made the original Consumption plan awkward: poor virtual network support and unpredictable cold starts.

az functionapp create \
  --name func-shop --resource-group rg-shop-prod-uks \
  --storage-account stshopfunc \
  --flexconsumption-location uksouth \
  --runtime dotnet-isolated --runtime-version 8.0 \
  --instance-memory 2048

What a cold start is

When no instance is running, a request must wait for one to be allocated, the runtime to start, and your code to load.

Warm instance   milliseconds
Cold start      hundreds of milliseconds to several seconds

The size varies with runtime, dependency count and package size. A large .NET app with many dependencies is slower to start than a small Node function.

It matters for a user facing HTTP API and matters far less for a queue processor, where a two second delay on the first message is invisible.

Always ready instances

Flex Consumption keeps a chosen number of instances warm.

az functionapp scale config always-ready set \
  --name func-shop --resource-group rg-shop-prod-uks \
  --settings http=2

Two instances always warm for HTTP triggers, scaling beyond that on demand. You pay for those two continuously and the rest per use.

This is the middle ground people wanted: no cold start for normal traffic, no cost for idle capacity above it.

az functionapp scale config always-ready set \
  --name func-shop --resource-group rg-shop-prod-uks \
  --settings durable=1 blob=1

Per trigger group, so a warm instance can be kept only where latency matters.

Concurrency

az functionapp scale config set \
  --name func-shop --resource-group rg-shop-prod-uks \
  --trigger-type http --trigger-settings perInstanceConcurrency=16

How many concurrent executions one instance handles. This is the lever people miss.

Too low   more instances than needed, more cold starts, higher cost
Too high  instances run out of memory or CPU, latency rises

A function doing IO and waiting can handle high concurrency. A function doing CPU work cannot. Start with the default, look at memory and duration, and adjust.

Instance memory

az functionapp create ... --instance-memory 4096

Flex Consumption offers 512 MB to 4096 MB per instance. Larger instances get proportionally more CPU, so a slow function is sometimes fixed by more memory rather than more instances.

Premium plan

az functionapp plan create \
  --name plan-func-premium --resource-group rg-shop-prod-uks \
  --location uksouth --sku EP1 --min-instances 1 --max-burst 20
Pre warmed instances, so no cold start
Unlimited execution duration
Virtual network integration
Predictable pricing, and you pay for the minimum whether used or not

Premium made more sense before Flex Consumption existed. Now it fits when you need unlimited execution duration, or when you want the App Service style plan model.

Execution limits

Consumption      5 minutes default, 10 maximum
Flex Consumption 30 minutes default, configurable
Premium          30 minutes default, unlimited configurable
Dedicated        30 minutes default, unlimited with Always On
{
  "version": "2.0",
  "functionTimeout": "00:10:00"
}

A function approaching its limit is usually the wrong shape. Split it, or use Durable Functions to break the work into activities that each finish quickly.

Scaling behaviour

HTTP           scales on request rate and concurrency
Queue          scales on queue length
Service Bus    scales on message count and active connections
Event Hubs     scales with partition count, so partitions cap parallelism
Timer          a single instance, never scales out

Event Hubs deserves attention: you cannot process a hub in parallel beyond its partition count. A hub with four partitions runs at most four instances of the function, no matter the backlog. Partition count is set at creation and, on most tiers, cannot be increased later.

The storage account

Every function app needs one. It holds trigger state, timer schedules, Durable Functions history and the deployment package.

Do not delete it
Do not let someone clear its containers
Do not share it with a busy application workload
Restricting its network access breaks the app unless done carefully

Deleting or wiping this storage account breaks the function app in ways that are hard to diagnose, because the failure shows up as triggers silently not firing.

Scale limits

az functionapp scale config set \
  --name func-shop --resource-group rg-shop-prod-uks \
  --maximum-instance-count 40

Set a maximum. A runaway function scaling to hundreds of instances against a database with a connection limit takes the database down, and the bill is unpleasant.

The maximum is a safety valve, not a performance setting.

Choosing

User facing API, spiky        Flex Consumption with always ready instances
Background queue processing   Flex Consumption, no always ready needed
Long running work             Premium, or Durable Functions
Already have an App Service plan with headroom   Dedicated
Functions alongside containers  Container Apps hosting
Legacy app on Consumption     migrate to Flex Consumption

Watching it

az monitor metrics list \
  --resource /subscriptions/.../sites/func-shop \
  --metric FunctionExecutionCount FunctionExecutionUnits \
  --interval PT1H --output table
requests
| where cloud_RoleName == "func-shop"
| summarize count(), avg(duration), percentile(duration, 95) by name
| order by count_ desc

FunctionExecutionUnits is what billing is based on, combining memory and time. A function using more memory than it needs costs more for the same work, which is worth checking before assuming the plan is the problem.

Quick recap

  • Flex Consumption is the modern default, and fixed the networking and cold start problems.
  • Always ready instances remove cold starts for the traffic you expect, without paying for idle capacity above it.
  • Per instance concurrency is the lever people miss, and it depends on whether the work is IO or CPU bound.
  • Event Hubs parallelism is capped by partition count, which is fixed at creation.
  • The function app’s storage account holds trigger state, so deleting or wiping it breaks things quietly.
  • Always set a maximum instance count, to protect downstream systems and the bill.
  • A function near its timeout is usually the wrong shape, so split it or use Durable Functions.
deploy.sh

# THE PLANS:
#   Flex Consumption  pay per use, scales to zero, vnet, always ready
#                     instances, configurable concurrency. THE DEFAULT NOW.
#   Consumption       the original. Limited networking, unpredictable starts.
#   Premium           pre warmed, unlimited duration, pay for the minimum
#   Dedicated         on an App Service plan you already pay for
az functionapp create --name func-shop --resource-group rg-shop-prod-uks \
  --storage-account stshopfunc --flexconsumption-location uksouth \
  --runtime dotnet-isolated --runtime-version 8.0 --instance-memory 2048
# COLD START: with no instance running, a request waits for allocation, the
# runtime and your code. Milliseconds warm, seconds cold. It matters for a
# user facing API and barely at all for a queue processor.
# ALWAYS READY INSTANCES are the middle ground people wanted: no cold start
# for normal traffic, no cost for idle capacity above it.
az functionapp scale config always-ready set --name func-shop \
  --resource-group rg-shop-prod-uks --settings http=2
# CONCURRENCY IS THE LEVER PEOPLE MISS.
#   too low   more instances than needed, more cold starts, higher cost
#   too high  instances run out of memory, latency rises
# IO bound work handles high concurrency. CPU bound work does not.
az functionapp scale config set --name func-shop --resource-group rg-shop-prod-uks \
  --trigger-type http --trigger-settings perInstanceConcurrency=16
# SCALING BEHAVIOUR BY TRIGGER:
#   HTTP        request rate and concurrency
#   Queue       queue length
#   Event Hubs  CAPPED BY PARTITION COUNT
#   Timer       a single instance, never scales out
# Four partitions means at most four instances, whatever the backlog, and
# partition count is fixed at creation on most tiers.
# THE STORAGE ACCOUNT holds trigger state, timer schedules and Durable
# history. Delete or wipe it and triggers SILENTLY STOP FIRING, which is
# very hard to diagnose. Do not share it with a busy workload.
# ALWAYS SET A MAXIMUM. A runaway function scaling to hundreds of instances
# takes down a database with a connection limit, and the bill is unpleasant.
az functionapp scale config set --name func-shop \
  --resource-group rg-shop-prod-uks --maximum-instance-count 40
# TIMEOUTS: Consumption 10 min max. Flex 30 default. Premium unlimited.
# A function NEAR its timeout is usually the wrong shape. Split it, or use
# Durable Functions so each activity finishes quickly.
# FunctionExecutionUnits combines MEMORY AND TIME, and is what you are
# billed on. An oversized function costs more for identical work.
az monitor metrics list --resource /subscriptions/.../sites/func-shop \
  --metric FunctionExecutionCount FunctionExecutionUnits --interval PT1H -o table

Was this explanation helpful?

Your feedback helps us improve the content for everyone.