Lambda Concurrency Controls
Four concurrency controls operate at different levels with non-obvious interactions. The exact numbers and mutual-exclusivity rules below are the part that's easy to get wrong.
The 4 concurrency types
Reserved Concurrency (scope: function) — sets the max concurrent instances AND reserves that capacity from the account pool. Setting to 0 fully throttles the function (emergency shutoff). Use to protect critical functions, cap to protect downstream, or kill-switch.
Provisioned Concurrency (scope: published version or alias — NOT
$LATEST) — pre-initializes environments so they're ready before requests arrive. Spills to on-demand (with cold starts) beyond the provisioned count. Combine with Application Auto Scaling (~70% target). Paid even when idle.Maximum Concurrency (scope: per SQS ESM, range 2–1,000) — caps how many concurrent instances one SQS ESM can invoke. Does not reserve anything; other triggers can still consume function concurrency.
Provisioned Mode — ESM (SQS and Kafka/MSK) — allocates dedicated event pollers for an SQS/Kafka ESM with configurable min/max. Per-poller capacity is an OR envelope and differs by source: SQS = 1 MB/s or 10 concurrent invokes; Kafka/MSK = 5 MB/s or 5 concurrent invokes. Use for high-throughput or spiky traffic where standard ramp-up (5 → +300/min) is too slow.
Key numbers and interactions
- Account RPS quota = 10 × account concurrency (e.g. 1,000 concurrency → 10,000 RPS, across all functions). This is an account quota, not a per-instance cap. Per-instance throughput = 1 / function duration.
- Max reservable = account limit − 100. Lambda always keeps 100 unreserved.
- Scaling rate: 1,000 new environments / 10s, per function
- Provisioned ≤ Reserved when both are set (reserved is the ceiling).
- Provisioned counts against the account limit even when idle — monitor
ClaimedAccountConcurrency.
| Combination | OK? | Notes |
|---|---|---|
| Reserved + Provisioned | Yes | Provisioned ≤ Reserved |
| Reserved + Maximum Concurrency (ESM) | Yes | Reserved ≥ Σ(Maximum Concurrency across ESMs) |
| Provisioned + Maximum Concurrency / Provisioned Mode (ESM) | Yes | Different layers |
| Maximum Concurrency + Provisioned Mode (same ESM) | No | Mutually exclusive |
| Provisioned Concurrency + SnapStart | No | Mutually exclusive |
At the limit: Sync → 429. Async → retries up to 6h then DLQ. Streams → polling throttled, messages stay in source.
RPS gotcha: a 50ms function at 20,000 RPS needs only 1,000 concurrency, but the RPS limit (10×1,000 = 10,000) throttles it. Request account concurrency = 2,000.
aws service-quotas request-service-quota-increase \
--service-code lambda --quota-code L-B99A9384 --desired-value 5000Decision scenarios
| Scenario | Reserved | Provisioned | Maximum Concurrency (ESM) | Prov Mode (ESM) |
|---|---|---|---|---|
| Protect critical API / cap downstream | Yes | — | — | — |
| Eliminate cold starts (user-facing API) | Optional | Yes | — | — |
| Multiple SQS queues, prevent hogging | Yes | — | Yes | — |
| High-throughput SQS, low-latency | Optional | Optional | — | Yes |
| Kafka/SQS ESM with spiky traffic | — | — | — | Yes |
| Predictable daily traffic | — | Yes + AutoScale | — | — |
| Emergency shutoff | Yes (=0) | — | — | — |
Common mistakes
- Reserved = 0 left over from an incident — blocks ALL invocations (429). If a function throttles at low traffic, check this first.
- Reserved too low — reserve 50, need 80 → throttled at 51 even with spare account capacity.
- Starving other functions — reserved is subtracted even when unused; be conservative.
- Provisioned without auto scaling — paying for idle off-peak, spilling on-peak.
- Provisioned on
$LATEST— doesn't work; publish a version, create an alias. - Maximum Concurrency > reserved — ESM tries 100, function caps at 50. Ensure reserved ≥ Σ(Maximum Concurrency).
- Confusing ESM Maximum Concurrency with reserved — Maximum Concurrency reserves nothing; API Gateway can still consume all concurrency.
- Forgetting the 100-unit buffer — max reservable = account limit − 100.
SnapStart vs Provisioned Concurrency
Mutually exclusive on the same function.
Runtime Java 11+ / Python 3.12+ / .NET 8+
├─ No → Provisioned Concurrency
└─ Yes
├─ Need guaranteed <50ms on EVERY request? → Provisioned Concurrency
├─ Need EFS or >512MB ephemeral storage? → Provisioned Concurrency
└─ Otherwise → SnapStart first; if P99 still too high, switch (they cannot coexist)SAM/CDK property reference
| Type | SAM | CDK |
|---|---|---|
| Reserved | ReservedConcurrentExecutions |
reservedConcurrentExecutions |
| Provisioned | AutoPublishAlias + ProvisionedConcurrencyConfig.ProvisionedConcurrentExecutions |
new lambda.Alias({ provisionedConcurrentExecutions }) (alias, not $LATEST) |
| Maximum Concurrency (ESM) | ScalingConfig.MaximumConcurrency |
maxConcurrency on EventSourceMapping |
| Provisioned Mode (ESM) | ProvisionedPollerConfig.MinimumPollers / MaximumPollers |
provisionedPollerConfig: { minimumPollers, maximumPollers } |
| SnapStart | SnapStart.ApplyOn: PublishedVersions + AutoPublishAlias |
snapStart: lambda.SnapStartConf.ON_PUBLISHED_VERSIONS |
Auto scaling: alias.addAutoScaling({ minCapacity, maxCapacity }) then scaling.scaleOnUtilization({ utilizationTarget: 0.7 }).