Questions
Azure Functions have 10-second cold starts causing API timeouts. Optimize for low latency.
The Scenario
Your Azure Functions HTTP API experiences severe cold starts:
Request timeline (cold start):
├── Function host initialization: 3s
├── .NET runtime startup: 2s
├── Dependency injection setup: 3s
├── Database connection pool: 2s
└── Actual function execution: 0.5s
Total: 10.5s (API Gateway timeout: 10s)
Users see intermittent timeouts, especially after periods of inactivity.
The Challenge
Reduce cold start times to under 2 seconds while balancing cost. Understand the factors affecting startup time and implement targeted optimizations.
A junior engineer might switch to Premium plan with always-on instances set to 100, use a timer trigger to ping the function every minute, or ignore the problem and increase timeouts. These approaches are expensive, wasteful, or don't solve the actual latency issue.
A senior engineer analyzes cold start components, optimizes application initialization, uses Premium plan with appropriate pre-warmed instances, implements lazy loading, and considers Durable Functions for long-running operations.
Step 1: Understand Cold Start Components
Cold Start Breakdown:
┌─────────────────────────────────────────────────┐
│ Platform (Azure) - Cannot control │
├─────────────────────────────────────────────────┤
│ • Instance allocation: ~500ms │
│ • Runtime initialization: ~500-1500ms │
│ • Extension loading: ~200-500ms │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Application (Your code) - CAN optimize │
├─────────────────────────────────────────────────┤
│ • Package restore/loading: ~500-2000ms │
│ • Dependency injection: ~200-3000ms │
│ • Static initialization: varies │
│ • Connection establishment: ~500-2000ms │
└─────────────────────────────────────────────────┘Step 2: Use Premium Plan with Pre-Warmed Instances
// Premium plan with pre-warmed instances
resource functionAppPlan 'Microsoft.Web/serverfarms@2022-09-01' = {
name: 'func-plan-premium'
location: location
sku: {
tier: 'ElasticPremium'
name: 'EP1'
}
properties: {
maximumElasticWorkerCount: 20
}
}
resource functionApp 'Microsoft.Web/sites@2022-09-01' = {
name: 'my-api-function'
location: location
kind: 'functionapp'
properties: {
serverFarmId: functionAppPlan.id
siteConfig: {
// Pre-warmed instances always ready
preWarmedInstanceCount: 2
// Minimum instances (never scale to zero)
functionAppScaleLimit: 10
// Runtime settings
netFrameworkVersion: 'v8.0'
use32BitWorkerProcess: false
// Keep instances warm longer
appSettings: [
{
name: 'WEBSITE_WARMUP_PATH'
value: '/api/health'
}
{
name: 'FUNCTIONS_WORKER_RUNTIME'
value: 'dotnet-isolated'
}
]
}
}
}Step 3: Optimize Application Code
// BEFORE: Slow startup with synchronous initialization
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
// Bad: Synchronous database connection
var dbConnection = new SqlConnection(connString);
dbConnection.Open(); // Blocks during cold start!
// Bad: Loading all configuration upfront
var config = LoadAllConfiguration(); // Blocks!
builder.Services.AddSingleton(dbConnection);
}
}
// AFTER: Lazy initialization
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
// Good: Lazy connection factory
builder.Services.AddSingleton<IDbConnectionFactory, LazyDbConnectionFactory>();
// Good: Configuration loaded on-demand
builder.Services.AddSingleton<IConfiguration>(sp =>
new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build());
// Good: Use AddScoped for per-request services
builder.Services.AddScoped<IMyService, MyService>();
}
}
public class LazyDbConnectionFactory : IDbConnectionFactory
{
private readonly Lazy<SqlConnection> _connection;
public LazyDbConnectionFactory(IConfiguration config)
{
_connection = new Lazy<SqlConnection>(() =>
{
var conn = new SqlConnection(config["SqlConnectionString"]);
conn.Open();
return conn;
});
}
public SqlConnection GetConnection() => _connection.Value;
}Step 4: Reduce Package Size
<!-- BEFORE: Large package with unused dependencies -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<!-- Bad: Not trimmed -->
</PropertyGroup>
<ItemGroup>
<!-- Bad: Heavy dependencies -->
<PackageReference Include="Entity.Framework.Core" Version="8.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>
<!-- AFTER: Optimized package -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<!-- Enable trimming -->
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
<!-- Ahead-of-time compilation -->
<PublishAot>true</PublishAot>
<!-- Single file deployment -->
<PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>
<ItemGroup>
<!-- Use lighter alternatives -->
<PackageReference Include="Dapper" Version="2.1.24" />
<PackageReference Include="System.Text.Json" Version="8.0.0" />
</ItemGroup>
</Project>Step 5: Use HTTP-Triggered Warmup
// Warmup function that initializes dependencies
[Function("Warmup")]
public async Task<HttpResponseData> Warmup(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "warmup")] HttpRequestData req)
{
// Touch lazy-loaded services to initialize them
_ = _dbConnectionFactory.GetConnection();
_ = _cacheService.GetClient();
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteStringAsync("Warmed up");
return response;
}
// Configure warmup path in app settings
// WEBSITE_WARMUP_PATH = /api/warmupStep 6: Consider Durable Functions for Long Operations
// For operations that might exceed timeout, use Durable Functions
[Function("ProcessOrder_Orchestrator")]
public async Task<OrderResult> ProcessOrderOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var orderId = context.GetInput<string>();
// These run asynchronously, don't block the HTTP response
var validation = await context.CallActivityAsync<bool>("ValidateOrder", orderId);
var payment = await context.CallActivityAsync<PaymentResult>("ProcessPayment", orderId);
var shipping = await context.CallActivityAsync<ShippingResult>("ScheduleShipping", orderId);
return new OrderResult(validation, payment, shipping);
}
[Function("ProcessOrder_Http")]
public async Task<HttpResponseData> ProcessOrderHttp(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client)
{
var orderId = await req.ReadFromJsonAsync<string>();
// Start orchestration and return immediately
var instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
"ProcessOrder_Orchestrator", orderId);
// Return 202 Accepted with status URL
return client.CreateCheckStatusResponse(req, instanceId);
}Step 7: Use Flex Consumption Plan (Preview)
// New Flex Consumption plan offers faster cold starts
resource functionAppPlan 'Microsoft.Web/serverfarms@2023-01-01' = {
name: 'func-plan-flex'
location: location
sku: {
tier: 'FlexConsumption'
name: 'FC1'
}
properties: {
reserved: true
}
}
// Flex benefits:
// - Faster cold starts (VNet integration without penalty)
// - Instance size options (2GB, 4GB memory)
// - Always-ready instances (like Premium but pay-per-use)
// - Concurrency control per instanceCold Start Comparison by Plan
| Plan | Typical Cold Start | Always-On Option | Cost |
|---|---|---|---|
| Consumption | 2-10+ seconds | No | Pay per execution |
| Premium EP1 | Under 1 second | Yes (pre-warmed) | ~$150/month base |
| Premium EP2 | Under 1 second | Yes (pre-warmed) | ~$300/month base |
| Flex Consumption | 1-3 seconds | Yes (always-ready) | Pay per execution + always-ready |
| Dedicated | None | Always on | App Service pricing |
Cold Start Optimization Checklist
| Optimization | Impact | Effort |
|---|---|---|
| Premium plan + pre-warmed | High | Low |
| Lazy dependency injection | High | Medium |
| Reduce package size | Medium | Medium |
| Use System.Text.Json | Low | Low |
| Enable AOT compilation | Medium | Medium |
| Warmup function | Medium | Low |
Practice Question
Why is lazy initialization of database connections important for reducing Azure Functions cold starts?