Provisioned concurrency keeps a set number of Lambda execution environments warm and ready, eliminating cold-start latency for latency-sensitive workloads. It's billed by the hour for the configured concurrency level, regardless of whether those environments are actually invoked — which means it's easy to configure once for a real latency problem and then keep paying for capacity long after traffic patterns change.
What Provisioned Concurrency Actually Costs
Provisioned concurrency bills a separate rate on top of standard Lambda invocation and duration charges, applied continuously for every configured concurrent execution environment, 24/7, whether invoked or not. For a function configured with 20 provisioned concurrent executions running around the clock, that's 20 environments billed continuously — a real, ongoing cost independent of actual traffic.
The Cold-Start Problem It's Meant to Solve
Cold starts happen when Lambda needs to initialize a new execution environment — downloading code, starting the runtime, running initialization code — before it can process an invocation. For latency-sensitive synchronous workloads (an API Gateway-backed endpoint with a tight SLA), a cold start of a few hundred milliseconds to a few seconds can be a real user-facing problem. Provisioned concurrency solves this by keeping environments pre-initialized.
When It's the Right Tool
- Synchronous, latency-sensitive endpoints with strict SLA requirements where even occasional cold starts violate the SLA
- Predictable traffic patterns where you can size provisioned concurrency to match actual concurrent load, rather than guessing
- Functions with heavy initialization code (large dependencies, database connection setup) where cold starts are particularly slow
When It's Quietly Wasting Money
import boto3
from datetime import datetime, timedelta
cloudwatch = boto3.client("cloudwatch")
lambda_client = boto3.client("lambda")
def audit_provisioned_concurrency_utilization(function_name):
"""Compares configured provisioned concurrency against actual
concurrent execution metrics to find over-provisioned functions."""
config = lambda_client.get_provisioned_concurrency_config(
FunctionName=function_name, Qualifier="live"
)
configured = config["RequestedProvisionedConcurrentExecutions"]
metrics = cloudwatch.get_metric_statistics(
Namespace="AWS/Lambda",
MetricName="ProvisionedConcurrentExecutions",
Dimensions=[{"Name": "FunctionName", "Value": function_name}],
StartTime=datetime.utcnow() - timedelta(days=14),
EndTime=datetime.utcnow(),
Period=3600,
Statistics=["Average", "Maximum"],
)
avg_used = sum(m["Average"] for m in metrics["Datapoints"]) / max(len(metrics["Datapoints"]), 1)
return {
"function": function_name,
"configured": configured,
"avg_actual_usage": round(avg_used, 2),
"utilization_pct": round((avg_used / configured) * 100, 1) if configured else 0,
}
Functions consistently showing utilization well under 50% of their configured provisioned concurrency are a strong signal the setting was configured for a traffic spike that either never fully materialized or has since declined, and the ongoing cost hasn't been revisited.
The Auto-Scaling Middle Ground
Application Auto Scaling can adjust provisioned concurrency on a schedule or based on utilization metrics, which handles predictable daily traffic patterns (busy business hours, quiet overnight) without requiring manual reconfiguration or accepting a flat rate sized for peak load around the clock.
A Cheaper Alternative Worth Testing First
Lambda SnapSnap (available for certain runtimes) can reduce cold-start latency without the continuous cost of provisioned concurrency, by caching a snapshot of an initialized execution environment. For functions where the latency requirement is real but not extremely strict, this is worth testing before committing to provisioned concurrency's ongoing cost.
Frequently Asked Questions
How often should we review provisioned concurrency settings?
Monthly is reasonable for functions with meaningful spend, especially after any change in traffic patterns, feature launch, or deprecation of a dependent service that used to drive load.
Does removing provisioned concurrency bring back the original cold-start problem immediately?
Yes — removing it reverts to standard on-demand execution environment initialization, so this should only be done after confirming the latency requirement that originally justified it either no longer applies or can tolerate standard cold starts.
Can auto-scaling schedules fully replace manual provisioned concurrency tuning?
For predictable traffic patterns, yes, largely. For genuinely unpredictable or bursty traffic, scheduled scaling won't react fast enough, and a different approach — like combining a smaller baseline with SnapStart — may fit better.
Is there a way to test the latency impact before committing to provisioned concurrency?
Yes — load testing the function under realistic concurrent traffic with and without provisioned concurrency enabled gives a direct before/after latency comparison to justify the ongoing cost.
See what an AWS cost audit finds in your Lambda spend → rutagon.com/contact or call 907-841-8407.