Module 3: Serverless & Lambda for AI
4. Cold Starts in AI
Overview
In this capsule you'll confront the most-debated problem of Lambda for AI: cold starts. Not with opinions — with real numbers. You'll understand what a cold start is, measure its impact with different dependencies (openai vs langchain vs numpy+pandas), and learn concrete mitigation strategies. By the end, you'll be able to calculate whether your function's cold start is acceptable for your use case and what to do if it isn't.
Context: The previous capsules taught you to write a Lambda function for AI (02) and to package it as a zip or container (03). In both cases we mentioned cold starts as a trade-off. Here you go deeper: how long do they really last? Why are they worse for AI? How do you mitigate them without spending a fortune? This is an engineering problem, not an abstract limitation.
What a Cold Start Is
The concept
A cold start happens when Lambda needs to create a new container to run your function. This happens when:
- First invocation after deploying
- No containers available (all are busy with other invocations)
- Expired container (Lambda recycles idle containers after ~5-15 minutes)
- Scaling up (traffic increases and Lambda needs more containers)
Cold start (new container):
┌─────────────────────────────────────────────────┐
│ 1. Provision container │ ~200-500ms
│ 2. Download code/image │ ~100ms-3s
│ 3. Initialize Python runtime │ ~100-200ms
│ 4. Run code outside the handler (imports) │ ~200ms-10s
│ 5. Run handler │ Variable
└─────────────────────────────────────────────────┘
Warm start (existing container):
┌─────────────────────────────────────────────────┐
│ 5. Run handler │ Variable
└─────────────────────────────────────────────────┘
The key difference: in a warm start, steps 1-4 are skipped. The container already exists, the runtime is loaded, your imports are in memory. Only the handler runs.
Why it matters more for AI
For a typical web function (returns HTML, processes a form), a 500ms cold start is imperceptible. For an AI function, the cold start adds to the inference time:
Typical web function:
├── Cold start: 500ms
├── Handler: 50ms
└── Total: 550ms (user waits ~0.5s)
AI function (openai SDK):
├── Cold start: 1.5s
├── Handler: 3s (call to GPT-4o-mini)
└── Total: 4.5s (user waits ~4.5s)
AI function (langchain + deps):
├── Cold start: 8s
├── Handler: 3s (call to GPT-4o-mini)
└── Total: 11s (user thinks the app is broken)
The handler takes the same in both cases (the LLM call is the same). The difference is that with heavy dependencies, the cold start doubles or triples the total time.
Real Measurement of Cold Starts
Methodology
To measure cold starts reliably, you need to:
- Force a cold start (update the function between invocations)
- Measure total time from when Lambda receives the event
- Separate init duration from handler duration
- Repeat 10+ times to average
Lambda reports the Init Duration in the CloudWatch logs — that's your cold start as measured by AWS.
Measurement tool
# measure_cold_start.py
# Script to measure cold starts consistently
import json
import time
import subprocess
import statistics
def force_cold_start(function_name):
"""Forces a cold start by updating an env var."""
subprocess.run([
"aws", "lambda", "update-function-configuration",
"--function-name", function_name,
"--environment", json.dumps({
"Variables": {
"OPENAI_API_KEY": "sk-test",
"COLD_START_MARKER": str(time.time()),
}
}),
], capture_output=True)
time.sleep(5)
def invoke_and_measure(function_name):
"""Invokes Lambda and returns total duration."""
payload = json.dumps({"body": json.dumps({"prompt": "Say hello"})})
start = time.time()
result = subprocess.run([
"aws", "lambda", "invoke",
"--function-name", function_name,
"--payload", payload,
"--log-type", "Tail",
"output.json",
], capture_output=True, text=True)
total = time.time() - start
return total
def run_benchmark(function_name, iterations=10):
"""Runs a cold-start benchmark."""
cold_starts = []
warm_starts = []
for i in range(iterations):
# Cold start
force_cold_start(function_name)
cold = invoke_and_measure(function_name)
cold_starts.append(cold)
# Warm start (immediate invocation, same container)
warm = invoke_and_measure(function_name)
warm_starts.append(warm)
print(f" Iteration {i+1}: cold={cold:.2f}s, warm={warm:.2f}s")
print(f"\nCold starts: avg={statistics.mean(cold_starts):.2f}s, "
f"p50={statistics.median(cold_starts):.2f}s, "
f"max={max(cold_starts):.2f}s")
print(f"Warm starts: avg={statistics.mean(warm_starts):.2f}s, "
f"p50={statistics.median(warm_starts):.2f}s, "
f"max={max(warm_starts):.2f}s")
print(f"Cold start overhead: {statistics.mean(cold_starts) - statistics.mean(warm_starts):.2f}s")
Results by dependency type
These are representative results for Lambda functions with 512MB of memory in us-east-1:
┌────────────────────────────────────────────────────────────────┐
│ Dependency │ Zip Cold │ Container Cold │ Warm │
├────────────────────────┼──────────┼────────────────┼──────────┤
│ No dependencies │ 0.3s │ 0.8s │ <0.1s │
│ openai SDK (~5MB) │ 0.8s │ 1.5s │ <0.1s │
│ openai + httpx (~8MB) │ 1.0s │ 1.8s │ <0.1s │
│ anthropic SDK (~8MB) │ 1.0s │ 1.7s │ <0.1s │
│ langchain (~80MB) │ 3.5s │ 5.0s │ <0.1s │
│ langchain + chromadb │ 5.0s │ 7.0s │ <0.1s │
│ numpy + pandas (~120MB)│ 4.0s │ 6.5s │ <0.1s │
│ numpy + sklearn │ 5.5s │ 8.0s │ <0.1s │
│ torch CPU (~800MB) │ N/A │ 15-25s │ <0.1s │
└────────────────────────┴──────────┴────────────────┴──────────┘
Memory: 512MB | Runtime: Python 3.11 | Region: us-east-1
N/A = doesn't fit in a zip (exceeds the 250MB limit)
What the data reveals
-
Warm starts are always <100ms. The cold start is a one-off event; once warm, Lambda responds fast.
-
The jump from openai to langchain is 4x. It's not linear with size — it depends on how many imports and how much initialization there is.
-
Container adds 1-2s over zip. The overhead is the image pull from ECR.
-
512MB of memory. With more memory, Lambda allocates more proportional CPU, and cold starts drop. With 1024MB, the numbers reduce ~30%.
-
torch is prohibitive. A 15-25s cold start makes Lambda unviable for local models. Use SageMaker or ECS.
Anatomy of the Cold Start
Where the time goes
# handler.py with timing logging
import time
_init_start = time.time()
import json # ~1ms
import os # ~1ms
_after_stdlib = time.time()
from openai import OpenAI # ~300ms (imports httpx, pydantic, etc.)
_after_openai = time.time()
# If you use langchain, add ~2-5s here
# from langchain_openai import ChatOpenAI
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", ""))
_after_client = time.time()
_init_total = _after_client - _init_start
print(f"INIT: stdlib={(_after_stdlib-_init_start)*1000:.0f}ms, "
f"openai={(_after_openai-_after_stdlib)*1000:.0f}ms, "
f"client={(_after_client-_after_openai)*1000:.0f}ms, "
f"total={_init_total*1000:.0f}ms")
def handler(event, context):
handler_start = time.time()
body = json.loads(event.get("body", "{}"))
prompt = body.get("prompt", "test")
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
max_tokens=100,
)
handler_time = time.time() - handler_start
return {
"statusCode": 200,
"body": json.dumps({
"answer": response.choices[0].message.content,
"init_ms": int(_init_total * 1000),
"handler_ms": int(handler_time * 1000),
}),
}
Typical output in CloudWatch:
INIT: stdlib=2ms, openai=312ms, client=45ms, total=359ms
Init Duration: 847ms (AWS overhead + your init)
Duration: 2456ms (handler execution)
Billed Duration: 3303ms (init + handler)
The hidden cost: transitive imports
from openai import OpenAI doesn't just import openai. It imports httpx, anyio, pydantic, certifi, and more. Each transitive import adds to the cold start:
openai imports:
├── httpx (~150ms)
│ ├── anyio
│ ├── httpcore
│ └── certifi
├── pydantic (~100ms)
│ └── pydantic-core
└── typing_extensions (~5ms)
Total: ~300ms
langchain imports:
├── langchain_core (~500ms)
│ ├── pydantic
│ ├── jsonpatch
│ └── tenacity
├── langchain (~1500ms)
│ ├── requests
│ ├── aiohttp
│ ├── SQLAlchemy
│ └── numpy (if installed)
├── langchain_openai (~200ms)
│ └── openai
└── dataclasses-json (~100ms)
Total: ~2500ms
Mitigation Strategies
Strategy 1: Package optimization
The first line of defense: include only what you need.
# Problem: pip install langchain brings EVERYTHING
pip install langchain
# Installs: langchain, langchain-core, langchain-text-splitters,
# SQLAlchemy, aiohttp, requests, numpy, pyyaml, ...
# Solution: install only the components you use
pip install langchain-core langchain-openai
# Installs: langchain-core, langchain-openai, openai, pydantic
# ~70% fewer dependencies
# requirements.txt — BEFORE (all of langchain)
langchain>=0.2.0
# requirements.txt — AFTER (only what's needed)
langchain-core>=0.2.0
langchain-openai>=0.1.0
Measured impact:
Full langchain: Cold start ~3.5s (zip) / ~5.0s (container)
langchain-core + openai: Cold start ~1.5s (zip) / ~2.5s (container)
Reduction: ~55%
Strategy 2: Lazy imports
Import heavy modules only when you need them, not at startup:
# ❌ Import at startup — penalizes ALL cold starts
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
def handler(event, context):
...
# ✅ Lazy import — only penalizes the first invocation that uses it
_chain = None
def _get_chain():
global _chain
if _chain is None:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", "Be concise."),
("user", "{input}"),
])
llm = ChatOpenAI(model="gpt-4o-mini")
_chain = prompt | llm
return _chain
def handler(event, context):
body = json.loads(event.get("body", "{}"))
if body.get("use_chain"):
chain = _get_chain()
result = chain.invoke({"input": body["prompt"]})
answer = result.content
else:
# Fast path without langchain
response = client.chat.completions.create(...)
answer = response.choices[0].message.content
...
Advantage: if most invocations use the path without LangChain, the cold start doesn't include those imports.
Strategy 3: More memory = more CPU = faster cold start
Lambda allocates CPU proportional to the configured memory. More memory → more CPU → faster imports:
langchain cold start by memory level:
├── 128MB: ~6.0s (minimum CPU)
├── 256MB: ~4.5s
├── 512MB: ~3.5s
├── 1024MB: ~2.5s (sweet spot for AI)
├── 1769MB: ~2.0s (1 full vCPU)
├── 3008MB: ~1.5s
└── 10240MB: ~1.2s (diminishing returns)
The sweet spot for AI functions is 1024-1769MB: enough CPU for fast imports without paying for memory you don't use at runtime.
# Change memory
aws lambda update-function-configuration \
--function-name ai-endpoint \
--memory-size 1024
# Lambda Power Tuning — tool to find the optimum
# https://github.com/alexcasalboni/aws-lambda-power-tuning
Strategy 4: Provisioned Concurrency
Keeps N containers always warm. Zero cold starts for those N containers.
# Configure 5 always-warm containers
aws lambda put-provisioned-concurrency-config \
--function-name ai-endpoint \
--qualifier prod \
--provisioned-concurrent-executions 5
Without provisioned concurrency:
├── Invocations 1-N: Cold start (~3s each)
├── Following invocations: Warm (~0.1s)
└── After inactivity: Cold start again
With provisioned concurrency (5):
├── 5 simultaneous invocations 1-5: Warm (~0.1s) ← always
├── Invocation 6+: Cold start (overflow)
└── Cost: you pay for the 5 containers 24/7
Cost of provisioned concurrency:
Provisioned: $0.0000041667/GB-second (always running)
On-demand: $0.0000166667/GB-second (only when it runs)
5 containers × 512MB × 24h × 30 days:
5 × 0.5GB × 86400s × 30 × $0.0000041667 = ~$27/month
That's $27/month to eliminate cold starts on the first 5 concurrent invocations.
Is it worth it? Depends on your case:
| Scenario | Provisioned? | Reason |
|---|---|---|
| Chatbot with active users | ✅ Yes | UX matters, cold start visible |
| API with a latency SLA | ✅ Yes | SLA doesn't tolerate 3-5s extra |
| Nightly batch processing | ❌ No | No user waiting |
| MVP with <100 invocations/day | ❌ No | Cost not justified |
| API with constant traffic | ✅ Yes | Containers stay warm naturally |
Strategy 5: Keep-warm (periodic ping)
A CloudWatch Event that invokes your Lambda every 5 minutes to keep containers warm:
# In SAM template
Resources:
AIEndpoint:
Type: AWS::Serverless::Function
Properties:
Handler: handler.handler
# ... config ...
Events:
KeepWarm:
Type: Schedule
Properties:
Schedule: rate(5 minutes)
Input: '{"source": "keep-warm"}'
def handler(event, context):
# Detect keep-warm invocation and return quickly
if event.get("source") == "keep-warm":
return {"statusCode": 200, "body": "warm"}
# Normal logic
...
Limitations:
- Only keeps 1 container warm. If you have concurrent invocations, the additional ones still have a cold start.
- Lambda can recycle the container even with pings (it's not a guarantee).
- Cost: keep-warm invocations are charged (but they're very cheap because they last <100ms).
Keep-warm: 12 invocations/hour × 24h × 30 days = 8640 invocations
× 100ms × 512MB = ~$0.05/month
vs Provisioned concurrency: ~$27/month for 5 containers
Keep-warm is ~500x cheaper but only keeps 1 container.
Comparison of Mitigation Strategies
| Strategy | Cold start reduction | Additional cost | Complexity | When |
|---|---|---|---|---|
| Optimize package | 30-60% | $0 | Low | Always (first action) |
| Lazy imports | 20-40% (conditional) | $0 | Low | When you have optional paths |
| More memory | 20-50% | Varies | Low | When the cold start is CPU-bound |
| Provisioned concurrency | 100% (N containers) | $5-50+/month | Medium | Production with SLA |
| Keep-warm | ~100% (1 container) | ~$0.05/month | Low | MVP, low traffic |
Recommended combination for AI
Level 1 (free, always):
├── Optimize dependencies (only what you use)
├── Lazy imports for optional paths
└── Memory ≥ 512MB for AI functions
Level 2 (low traffic, ~$0.05/month):
└── Keep-warm every 5 minutes
Level 3 (production, $5-50/month):
└── Provisioned concurrency (N according to concurrent traffic)
Cold Starts in Context: Is It Really a Problem?
When the cold start does NOT matter
- Batch processing: No one is waiting. A 10s cold start in a 2-minute job is irrelevant.
- Webhooks and event processing: The event is processed in the background. 3s extra don't affect it.
- Constant traffic: If your Lambda receives invocations every few seconds, the containers stay warm naturally. Cold starts are rare.
- Async processing: The user gets an immediate "202 Accepted." The cold start doesn't affect their experience.
When the cold start DOES matter
- Chatbots and synchronous APIs: The user sees a spinner during cold start + inference. 8s total feels slow.
- Traffic spikes: When traffic rises suddenly, Lambda needs to create many new containers → many simultaneous cold starts.
- Latency SLAs: If you promised p99 < 5s, a 3s cold start + 3s handler = 6s > SLA.
Decision framework
Does your user wait for the response in real time?
├── NO → Cold starts don't matter. Don't invest in mitigation.
└── YES → How long is your cold start?
├── <2s → Acceptable for most UX. Optimize the package.
├── 2-5s → Depends on the UX. Keep-warm may be enough.
└── >5s → You need provisioned concurrency or reconsider Lambda.
Does the cost of provisioned justify it?
├── YES → Provisioned concurrency
└── NO → Consider ECS/Fargate (always-on server)
Troubleshooting
Problem 1: "10s+ cold start with only the openai SDK"
# Check the configured memory
aws lambda get-function-configuration \
--function-name ai-endpoint \
--query 'MemorySize'
# If it's 128MB, that's the problem — there isn't enough CPU
# Bump to 512MB minimum for AI functions
aws lambda update-function-configuration \
--function-name ai-endpoint \
--memory-size 512
Problem 2: "Cold starts are inconsistent (sometimes 2s, sometimes 8s)"
# Lambda may provision your container on different hardware types.
# The variance is normal. Measure with 10+ invocations and use the p50/p95.
# Check in CloudWatch Logs
aws logs filter-log-events \
--log-group-name /aws/lambda/ai-endpoint \
--filter-pattern "Init Duration"
# Also check that you're not hitting a concurrency limit
aws lambda get-function \
--function-name ai-endpoint \
--query 'Concurrency'
Problem 3: "Provisioned concurrency is active but I still see cold starts"
# Check the number of provisioned vs concurrent invocations
aws lambda get-provisioned-concurrency-config \
--function-name ai-endpoint \
--qualifier prod
# If you have 5 provisioned but 8 simultaneous invocations,
# 3 will have a cold start. Increase provisioned or implement rate limiting.
Problem 4: "Keep-warm works but at noon there are cold starts"
Lambda can create new containers when traffic increases. Keep-warm only keeps 1 container. If at noon you have 5 concurrent invocations, 4 will have a cold start.
09:00 - 1 invocation → warm (keep-warm container)
09:01 - 1 invocation → warm
12:00 - 5 simultaneous → 1 warm + 4 cold starts
12:01 - 3 simultaneous → 3 warm (containers from the previous spike)
Solution: provisioned concurrency tuned to the expected peak, or accept the cold starts during spikes.
Hands-On Exercises
Exercise 1: Measure your cold start
Create a Lambda function with the openai SDK (zip deployment). Invoke it 5 times forcing a cold start and 5 times warm. Document the results in a table.
See solution
# Create the function (if it doesn't exist)
# Use the build.sh from capsule 03
# Simplified measurement script
for i in $(seq 1 5); do
echo "--- Cold start $i ---"
# Force a cold start by changing an env var
aws lambda update-function-configuration \
--function-name ai-endpoint-zip \
--environment "Variables={OPENAI_API_KEY=$OPENAI_API_KEY,MARKER=$i}" \
--query 'LastUpdateStatus' --output text
sleep 8
# Invoke and capture Init Duration
aws lambda invoke \
--function-name ai-endpoint-zip \
--payload '{"body": "{\"prompt\": \"Say hi\"}"}' \
--log-type Tail \
--query 'LogResult' \
--output text out.json | base64 -d | grep -E "Init Duration|Duration|Billed"
echo "--- Warm start $i ---"
# Invoke immediately (warm)
aws lambda invoke \
--function-name ai-endpoint-zip \
--payload '{"body": "{\"prompt\": \"Say hi\"}"}' \
--log-type Tail \
--query 'LogResult' \
--output text out.json | base64 -d | grep -E "Duration|Billed"
done
Expected result (example):
| # | Cold Start (Init) | Cold Total | Warm Total |
|---|-------------------|------------|------------|
| 1 | 823ms | 3.2s | 2.4s |
| 2 | 791ms | 3.1s | 2.3s |
| 3 | 856ms | 3.3s | 2.5s |
| 4 | 812ms | 3.2s | 2.4s |
| 5 | 834ms | 3.1s | 2.3s |
| **Avg** | **823ms** | **3.18s** | **2.38s** |
Cold start overhead: 3.18 - 2.38 = 0.80s
Exercise 2: Optimize the dependencies
Take a function that uses full langchain and replace it with only the components you need (langchain-core, langchain-openai). Measure the cold start before and after.
See solution
# BEFORE: requirements.txt
echo "langchain>=0.2.0
langchain-openai>=0.1.0" > requirements-before.txt
pip install -r requirements-before.txt -t before-package/ \
--platform manylinux2014_x86_64 \
--only-binary=:all: \
--python-version 3.11
du -sh before-package/
# ~85MB
# AFTER: optimized requirements.txt
echo "langchain-core>=0.2.0
langchain-openai>=0.1.0" > requirements-after.txt
pip install -r requirements-after.txt -t after-package/ \
--platform manylinux2014_x86_64 \
--only-binary=:all: \
--python-version 3.11
du -sh after-package/
# ~35MB
echo "Reduction: $(echo "scale=0; (85-35)*100/85" | bc)%"
# ~59% reduction in size
# The handler stays the same if you only use ChatOpenAI and ChatPromptTemplate
# Those are in langchain-core and langchain-openai
# Deploy both versions and measure cold starts
# Before: Init ~2.5-3.5s
# After: Init ~1.0-1.5s
Documented result:
| Version | Deps size | Cold start (avg) | Warm start (avg) |
|---------|-------------|------------------|------------------|
| full langchain | 85MB | 3.5s | 2.4s |
| langchain-core+openai | 35MB | 1.5s | 2.4s |
| **Improvement** | **59%** | **57%** | **0%** |
The warm start doesn't change — the improvement is 100% on cold start.
Exercise 3: Implement keep-warm
Configure a CloudWatch Event that invokes your Lambda every 5 minutes. Modify the handler to detect keep-warm invocations and return immediately.
See solution
# handler.py with keep-warm support
import json
import os
import time
from openai import OpenAI
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
def handler(event, context):
# Detect keep-warm invocation and return quickly
if event.get("source") == "keep-warm":
return {
"statusCode": 200,
"body": json.dumps({
"status": "warm",
"remaining_ms": context.get_remaining_time_in_millis(),
}),
}
# Normal logic
try:
body = json.loads(event.get("body", "{}"))
except json.JSONDecodeError:
return _response(400, {"error": "Invalid JSON"})
prompt = body.get("prompt", "").strip()
if not prompt:
return _response(400, {"error": "prompt required"})
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
max_tokens=500,
)
return _response(200, {
"answer": response.choices[0].message.content,
"tokens": response.usage.total_tokens,
})
def _response(status, body):
return {
"statusCode": status,
"headers": {"Content-Type": "application/json", "Access-Control-Allow-Origin": "*"},
"body": json.dumps(body),
}
# template.yaml with keep-warm event
Resources:
AIEndpoint:
Type: AWS::Serverless::Function
Properties:
Handler: handler.handler
Runtime: python3.11
CodeUri: lambda-function/
Timeout: 60
MemorySize: 512
Environment:
Variables:
OPENAI_API_KEY: !Ref OpenAIKey
Events:
AskAPI:
Type: Api
Properties:
Path: /ask
Method: post
KeepWarm:
Type: Schedule
Properties:
Schedule: rate(5 minutes)
Input: '{"source": "keep-warm"}'
Description: "Keeps Lambda warm to avoid cold starts"
Parameters:
OpenAIKey:
Type: String
NoEcho: true
sam build && sam deploy --guided
# Verify that keep-warm works
# In CloudWatch Logs, every 5 minutes you should see:
# {"status": "warm", "remaining_ms": 59800}
Exercise 4: Calculate the mitigation cost
For your AI function (512MB, 3s cold start), calculate and compare the monthly cost of three scenarios: no mitigation, with keep-warm, and with provisioned concurrency (3 containers). Assume 5000 real invocations/day.
See solution
## Base data
- Memory: 512MB (0.5GB)
- Real invocations: 5000/day = 150,000/month
- Average handler duration: 3s
- Cold start: 3s additional
- Traffic: ~70% between 9am-6pm (9 hours)
- Concurrency peak: ~5 simultaneous
## Scenario 1: No mitigation
- 150,000 invocations × 3s × 0.5GB = 225,000 GB-seconds
- ~5% cold starts (estimated with regular traffic): 7,500 cold starts
- Cold start extra: 7,500 × 3s × 0.5GB = 11,250 GB-seconds
- Total: 236,250 GB-seconds
- Compute cost: 236,250 × $0.0000166667 = ~$3.94
- Invocations cost: 150,000 × $0.20/1M = ~$0.03
- **Total: ~$3.97/month**
## Scenario 2: Keep-warm
- Real invocations: same ($3.97)
- Keep-warm: 12/hour × 24h × 30 days = 8,640 invocations
- Keep-warm cost: 8,640 × 0.1s × 0.5GB × $0.0000166667 = ~$0.007
- Reduced cold starts: ~1% (only during concurrency spikes)
- **Total: ~$3.98/month** (practically the same)
## Scenario 3: Provisioned Concurrency (3)
- On-demand compute (same): ~$3.97
- Provisioned: 3 × 0.5GB × 86,400s × 30 × $0.0000041667 = ~$16.20
- Cold starts: 0 for the first 3 concurrent
- **Total: ~$20.17/month**
## Summary
| Strategy | Cost/month | Cold starts | UX |
|-----------|----------|-------------|------|
| No mitigation | $3.97 | ~5% of invocations | 3s extra occasional |
| Keep-warm | $3.98 | ~1% (only spikes) | Good for low traffic |
| Provisioned (3) | $20.17 | ~0% (up to 3 conc.) | Consistent |
Conclusion: Keep-warm is almost-free and eliminates most cold starts.
Provisioned is worth it if the latency SLA justifies the extra $16/month.
Summary
- Cold starts happen when Lambda creates a new container. For AI functions, they can last 1-15s depending on the dependencies.
- The main impact is on UX: a 5s cold start + 3s handler = 8s that the user perceives as slow.
- Optimizing dependencies is the first action (free, -30-60% on cold start). Don't install
langchainwhen you only needlangchain-core. - More memory = more CPU = faster cold starts. The sweet spot for AI is 512MB-1024MB.
- Keep-warm (~$0.05/month) keeps 1 container warm. Enough for low traffic.
- Provisioned concurrency ($5-50+/month) eliminates cold starts for N containers. Necessary for production with SLAs.
- Warm starts are always <100ms. Cold starts are a one-off problem, not a constant one.
- Not everything needs mitigation. Batch and async processing aren't affected by cold starts.
Additional Resources
- Lambda Cold Starts — Official Docs — How Lambda manages execution environments
- Provisioned Concurrency — Official documentation on provisioned concurrency
- Lambda Power Tuning — Tool to find the optimal memory/cost
- Understanding Lambda Cold Starts — Lumigo — Detailed analysis of cold starts with real data
- Lambda SnapStart — Cold start optimization (Java, but the concept applies)
- Serverless Cold Start Comparison — Cold start benchmark by runtime and memory
- AWS Lambda Pricing Calculator — To calculate provisioned concurrency costs
- Reducing Lambda Cold Starts — AWS Blog — Official best practices