Module 3: Serverless & Lambda for AI
7. Cost Estimation for Serverless AI
Overview
In this capsule you'll learn to calculate the real cost of running AI workloads on Lambda — not the theoretical cost from the documentation, but the number that shows up on your bill. By the end, you'll be able to estimate costs before deploying, compare Lambda vs VPS at different traffic levels, and know exactly when Lambda stops being the economical option.
Context: "Lambda is cheap" is true for lightweight workloads. But an AI function that takes 5 seconds per invocation with 768MB of memory has a very different cost profile than a 100ms webhook with 128MB. Also, Lambda isn't the only cost — API Gateway, CloudWatch, and above all the OpenAI API are part of the bill. This capsule teaches you to see the full picture.
Lambda Pricing Model
The three components
Lambda bill = Requests + Compute + (Provisioned Concurrency)
1. Requests: $0.20 per million invocations
2. Compute: $0.0000166667 per GB-second (x86)
$0.0000133334 per GB-second (arm64 — 20% less)
3. Free tier: 1M requests + 400,000 GB-s/month (always free)
What a GB-second is
GB-second = (Memory in GB) × (Duration in seconds)
Example:
- Function with 768MB that takes 3 seconds
- GB-s = 0.75 GB × 3s = 2.25 GB-s
- Compute cost = 2.25 × $0.0000166667 = $0.0000375
AI example:
- Function with 768MB that takes 8 seconds (slow LLM call)
- GB-s = 0.75 GB × 8s = 6.0 GB-s
- Compute cost = 6.0 × $0.0000166667 = $0.0001000
The same function costs 2.67x more when the LLM takes longer!
Billed Duration
Lambda rounds the duration to the nearest millisecond (minimum 1ms):
Real duration: 2,345.6ms → Billed: 2,346ms (rounded to the ms)
Real duration: 0.3ms → Billed: 1ms (minimum)
Cost Calculator for AI Workloads
Scenario 1: Internal AI Chatbot (low traffic)
Parameters:
├── Invocations: 1,000/day = 30,000/month
├── Memory: 768MB (0.75 GB)
├── Average duration: 4s (gpt-4o-mini, ~300 tokens)
├── Architecture: arm64
Lambda calculation:
├── Requests: 30,000 / 1,000,000 × $0.20 = $0.006
├── GB-seconds: 30,000 × 0.75 × 4 = 90,000 GB-s
├── Compute: 90,000 × $0.0000133334 = $1.20
├── Free tier: -400,000 GB-s → 0 billable GB-s
└── Total Lambda: $0.006 (requests only, compute in free tier)
OpenAI cost (the REAL cost):
├── Input: 30,000 × 100 tokens × $0.00015/1K = $0.45
├── Output: 30,000 × 300 tokens × $0.0006/1K = $5.40
└── Total OpenAI: $5.85/month
API Gateway (HTTP API):
└── 30,000 × $1.00/million = $0.03
CloudWatch Logs:
└── ~$0.50/month (ingestion + storage)
═══════════════════════════════════════
MONTHLY TOTAL: ~$6.39
├── Lambda: $0.01 (negligible)
├── OpenAI: $5.85 (92% of the cost)
├── API Gateway: $0.03
└── CloudWatch: $0.50
═══════════════════════════════════════
Scenario 2: Public API (medium traffic)
Parameters:
├── Invocations: 10,000/day = 300,000/month
├── Memory: 768MB
├── Average duration: 5s (mix of gpt-4o-mini and gpt-4o)
├── Architecture: arm64
Lambda calculation:
├── Requests: 300,000 / 1M × $0.20 = $0.06
├── GB-seconds: 300,000 × 0.75 × 5 = 1,125,000 GB-s
├── Free tier: 1,125,000 - 400,000 = 725,000 billable GB-s
├── Compute: 725,000 × $0.0000133334 = $9.67
└── Total Lambda: $9.73
OpenAI cost:
├── 70% gpt-4o-mini: 210,000 × 400 tokens avg
│ Input: 210,000 × 100 × $0.00015/1K = $3.15
│ Output: 210,000 × 300 × $0.0006/1K = $37.80
├── 30% gpt-4o: 90,000 × 500 tokens avg
│ Input: 90,000 × 150 × $0.0025/1K = $33.75
│ Output: 90,000 × 350 × $0.01/1K = $315.00
└── Total OpenAI: $389.70/month
API Gateway:
└── 300,000 × $1.00/M = $0.30
CloudWatch:
└── ~$3.00/month
═══════════════════════════════════════
MONTHLY TOTAL: ~$402.73
├── Lambda: $9.73 (2.4%)
├── OpenAI: $389.70 (96.8%)
├── API Gateway: $0.30
└── CloudWatch: $3.00
═══════════════════════════════════════
Scenario 3: Production (high traffic)
Parameters:
├── Invocations: 100,000/day = 3,000,000/month
├── Memory: 1024MB
├── Average duration: 6s
├── Architecture: arm64
├── Provisioned concurrency: 10 instances
Lambda calculation:
├── Requests: 3,000,000 / 1M × $0.20 = $0.60
├── GB-seconds: 3,000,000 × 1.0 × 6 = 18,000,000 GB-s
├── Free tier: 18,000,000 - 400,000 = 17,600,000 GB-s
├── Compute: 17,600,000 × $0.0000133334 = $234.67
├── Provisioned: 10 × 1.0GB × 2,592,000s/month × $0.0000041667 = $108.00
└── Total Lambda: $343.27
OpenAI (100% gpt-4o-mini to control costs):
├── Input: 3,000,000 × 100 × $0.00015/1K = $45.00
├── Output: 3,000,000 × 300 × $0.0006/1K = $540.00
└── Total OpenAI: $585.00/month
API Gateway:
└── 3,000,000 × $1.00/M = $3.00
CloudWatch:
└── ~$15.00/month
═══════════════════════════════════════
MONTHLY TOTAL: ~$946.27
├── Lambda: $343.27 (36.3%)
├── OpenAI: $585.00 (61.8%)
├── API Gateway: $3.00
└── CloudWatch: $15.00
═══════════════════════════════════════
The lesson
At low traffic: OpenAI is >90% of your bill. Lambda is free.
At high traffic: Lambda grows, but OpenAI is still the majority.
Lambda's cost is NEVER your main problem with AI workloads.
Lambda vs VPS: Comparison at Different Levels
The calculation
# Lambda vs VPS calculator
def lambda_monthly_cost(
invocations_per_month: int,
memory_mb: int = 768,
duration_s: float = 5.0,
architecture: str = "arm64"
) -> dict:
price_per_gb_s = 0.0000133334 if architecture == "arm64" else 0.0000166667
price_per_request = 0.20 / 1_000_000
free_tier_gb_s = 400_000
free_tier_requests = 1_000_000
gb_seconds = invocations_per_month * (memory_mb / 1024) * duration_s
billable_gb_s = max(0, gb_seconds - free_tier_gb_s)
billable_requests = max(0, invocations_per_month - free_tier_requests)
compute_cost = billable_gb_s * price_per_gb_s
request_cost = billable_requests * price_per_request
return {
"compute": round(compute_cost, 2),
"requests": round(request_cost, 2),
"total": round(compute_cost + request_cost, 2),
"gb_seconds": round(gb_seconds),
"cost_per_invocation": round((compute_cost + request_cost) / max(1, invocations_per_month), 6),
}
# Compare
for monthly in [10_000, 100_000, 500_000, 1_000_000, 5_000_000]:
cost = lambda_monthly_cost(monthly)
print(f"{monthly:>10,} inv/month → Lambda: ${cost['total']:>8.2f}")
# Result:
# 10,000 inv/month → Lambda: $ 0.00 (free tier)
# 100,000 inv/month → Lambda: $ 1.85
# 500,000 inv/month → Lambda: $ 22.00
# 1,000,000 inv/month → Lambda: $ 46.67
# 5,000,000 inv/month → Lambda: $ 244.00
VPS pricing (reference)
Provider Plan RAM CPU Price/month
────────────────────────────────────────────────────────
Hetzner CX22 4GB 2 vCPU $4.50
DigitalOcean Basic 4GB 2 vCPU $24.00
AWS EC2 t3.medium 4GB 2 vCPU $30.37
Render Starter 2GB 1 CPU $7.00
Railway Pro 8GB 8 vCPU $20.00 + usage
A $25/month VPS runs your FastAPI 24/7 with ~50-100 req/s of capacity.
Comparison table
Invocations/month Lambda (arm64) VPS ($25/month) Winner
──────────────────────────────────────────────────────────────
10,000 $0.00 $25.00 Lambda
50,000 $0.00 $25.00 Lambda
100,000 $1.85 $25.00 Lambda
300,000 $11.67 $25.00 Lambda
500,000 $22.00 $25.00 Lambda
750,000 $35.00 $25.00 VPS
1,000,000 $46.67 $25.00 VPS
5,000,000 $244.00 $25.00 VPS
Break-even: ~600,000-700,000 invocations/month
(with 768MB and 5s average duration)
Break-even visual:
Cost ($)
│
250 ┤ ╱ Lambda
│ ╱
200 ┤ ╱
│ ╱
150 ┤ ╱
│ ╱
100 ┤ ╱
│ ╱
50 ┤────────────────────╱────────────────────── VPS ($25)
│ ╱
0 ┤─────────────╱
└──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬─── Invocations (×100K)
0 1 2 3 4 5 6 7 8 9 10
Lambda is cheaper up to ~600K inv/month.
After that, a fixed VPS wins.
But the comparison isn't only money
Factor Lambda VPS
─────────────────────────────────────────────────
Cost at 0 traffic $0 $25/month
Scalability Automatic Manual
Cold starts 1-8s No
Availability 99.95% SLA Depends on you
Maintenance Zero Patches, updates, monitoring
Deploy Push code SSH, Docker, systemd
Concurrency 1000+ parallel Limited by CPU/RAM
Timeout 15 min No limit
GPU No Possible
Custom runtime Limited Full control
The Real Cost: All the Components
Complete breakdown for an AI endpoint
Lambda AI Endpoint — Real monthly cost:
Service Low (30K/month) Medium (300K/month) High (3M/month)
─────────────────────────────────────────────────────────────────────
Lambda compute $0.00* $9.73 $234.67
Lambda requests $0.01 $0.06 $0.60
Lambda provisioned $0.00 $0.00 $108.00
API Gateway $0.03 $0.30 $3.00
CloudWatch Logs $0.50 $3.00 $15.00
CloudWatch Metrics $0.00 $0.00 $3.00
X-Ray (if enabled) $0.00 $1.50 $15.00
Secrets Manager $0.40 $0.40 $0.40
─────────────────────────────────────────────────────────────────────
AWS Subtotal $0.94 $14.99 $379.67
OpenAI API** $5.85 $389.70 $585.00
─────────────────────────────────────────────────────────────────────
TOTAL $6.79 $404.69 $964.67
* Within the free tier
** The real cost of the LLM API dominates in every scenario
Hidden costs people forget
1. CloudWatch Logs
Lambda logs automatically to CloudWatch.
Ingestion: $0.50/GB. Storage: $0.03/GB/month.
If you log a lot of JSON → it grows fast.
Mitigation: filter by log level, use sampling for high-volume requests.
2. API Gateway
HTTP API: $1.00/million. Looks like nothing, but at 10M requests/month = $10.
REST API: $3.50/million. 3.5x more expensive.
3. Secrets Manager
$0.40/secret/month + $0.05/10K API calls.
For 1-2 secrets (API keys) → ~$1/month.
4. Data transfer
First 100GB/month free. After that: $0.09/GB.
AI responses are text → low volume → rarely a problem.
5. Provisioned Concurrency
You pay to keep instances "warm" 24/7.
10 instances × 768MB × 30 days = ~$83/month
Only use it if cold starts are unacceptable for your case.
Cost Optimization Strategies
1. Memory tuning
Problem: excessive memory for I/O-bound functions.
Before: 1769MB (1 vCPU), 5s duration
→ 1.73 GB × 5s = 8.65 GB-s × $0.0000133334 = $0.0001153/inv
After: 512MB (enough for API calls), 5.2s duration
→ 0.5 GB × 5.2s = 2.6 GB-s × $0.0000133334 = $0.0000347/inv
Savings: 70% per invocation.
At 300K inv/month: $34.59 → $10.40 = $24.19 saved/month.
2. Response caching
# Cache identical responses to avoid repeated LLM calls
import hashlib
import json
import os
import boto3
from openai import OpenAI
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"], timeout=25)
# Use DynamoDB as the cache (or Redis via ElastiCache)
dynamodb = boto3.resource("dynamodb")
cache_table = dynamodb.Table(os.environ.get("CACHE_TABLE", "ai-cache"))
CACHE_TTL = int(os.environ.get("CACHE_TTL", "3600"))
def get_cache_key(prompt, model, max_tokens):
raw = f"{prompt}:{model}:{max_tokens}"
return hashlib.sha256(raw.encode()).hexdigest()[:16]
def handler(event, context):
body = json.loads(event.get("body", "{}"))
prompt = body["prompt"]
model = body.get("model", "gpt-4o-mini")
max_tokens = body.get("max_tokens", 500)
cache_key = get_cache_key(prompt, model, max_tokens)
# Check cache
try:
cached = cache_table.get_item(Key={"pk": cache_key})
if "Item" in cached:
import time
if cached["Item"].get("ttl", 0) > time.time():
return {
"statusCode": 200,
"body": json.dumps({
"answer": cached["Item"]["answer"],
"cached": True,
"tokens_saved": int(cached["Item"]["tokens"]),
})
}
except Exception:
pass
# LLM call
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
max_tokens=max_tokens,
)
answer = response.choices[0].message.content
tokens = response.usage.total_tokens
# Store in cache
import time
try:
cache_table.put_item(Item={
"pk": cache_key,
"answer": answer,
"tokens": tokens,
"ttl": int(time.time()) + CACHE_TTL,
})
except Exception:
pass
return {
"statusCode": 200,
"body": json.dumps({
"answer": answer,
"cached": False,
"tokens_used": tokens,
})
}
Caching impact:
If 30% of requests are duplicated:
├── Without cache: 300,000 LLM calls/month = $389.70 OpenAI
├── With cache: 210,000 LLM calls/month = $272.79 OpenAI
├── DynamoDB cache: ~$2.50/month (on-demand)
└── Savings: $114.41/month (29% less on OpenAI)
3. Smart model selection
# Use gpt-4o-mini by default, gpt-4o only when the user requests it
# The cost difference is ~17x
# gpt-4o-mini: $0.15/1M input + $0.60/1M output
# gpt-4o: $2.50/1M input + $10.00/1M output
def select_model(prompt, user_tier="free"):
if user_tier == "premium":
return "gpt-4o"
# For simple prompts, gpt-4o-mini is enough
if len(prompt) < 200:
return "gpt-4o-mini"
return "gpt-4o-mini"
4. Batching
If you process multiple items, group them into a single invocation
instead of one Lambda per item.
Without batching: 1000 items → 1000 invocations → 1000 × overhead
With batching: 1000 items → 10 invocations of 100 items → 10 × overhead
Lambda's overhead (cold start, init, API Gateway) is amortized.
Limitation: Lambda's 15-min timeout and API Gateway's 29s.
5. arm64 (Graviton)
Simply switching from x86_64 to arm64:
├── x86: $0.0000166667/GB-s
├── arm64: $0.0000133334/GB-s
├── Savings: 20% on compute
└── Compatibility: the openai SDK works perfectly on arm64
In the 300K inv/month example with 512MB:
├── x86: 780,000 GB-s × $0.0000166667 = $13.00
├── arm64: 780,000 GB-s × $0.0000133334 = $10.40
└── Savings: $2.60/month (20%)
Break-even Analysis
When Lambda stops being the economical option
def break_even_analysis():
"""Calculates the Lambda vs VPS break-even point."""
vps_cost = 25.00 # Monthly VPS (Hetzner/DO)
memory_gb = 0.75 # 768MB
duration_s = 5.0
price_per_gb_s = 0.0000133334 # arm64
free_tier_gb_s = 400_000
# Find the point where Lambda > VPS
for inv_k in range(0, 2000, 50):
invocations = inv_k * 1000
gb_s = invocations * memory_gb * duration_s
billable = max(0, gb_s - free_tier_gb_s)
lambda_cost = billable * price_per_gb_s + (invocations / 1_000_000) * 0.20
if lambda_cost > vps_cost:
print(f"Break-even: ~{invocations:,} invocations/month")
print(f" Lambda: ${lambda_cost:.2f}")
print(f" VPS: ${vps_cost:.2f}")
return invocations
return None
# break_even_analysis()
# Break-even: ~650,000 invocations/month
# Lambda: $25.34
# VPS: $25.00
The decision table
Invocations/month Lambda or VPS?
────────────────────────────────────────────────────────────
< 100K Lambda (free tier covers almost everything)
100K - 500K Lambda (cheaper, zero maintenance)
500K - 700K Gray zone (similar cost, choose by features)
> 700K VPS (Lambda already costs more)
> 2M VPS (Lambda is 5-10x more expensive)
BUT factor in these:
├── Do you need automatic scaling? → Lambda always wins
├── Are cold starts unacceptable? → VPS wins
├── Zero maintenance? → Lambda wins
├── GPU for local inference? → VPS is the only option
├── Unpredictable request spikes? → Lambda handles spikes for free
└── Fixed budget? → VPS is predictable; Lambda is variable
The irregular-traffic factor
Lambda shines when the traffic is irregular:
Pattern: 100K requests/month, but 80% during business hours (8h/day)
VPS: pays $25/month 24/7, even while you sleep → $25/month
Lambda: pays only for what you use → ~$1.85/month
Pattern: 500K requests/month, distributed 24/7
VPS: needs capacity for spikes → $25/month
Lambda: each individual invocation → $22/month
But if the traffic is constant and high:
Lambda: 1M requests/month × 5s × 768MB = $46.67/month
VPS: handles everything with a $25/month server
Tool: AWS Pricing Calculator
# Use the official calculator for precise estimates:
# https://calculator.aws/
# Or calculate in your code:
python3 -c "
memory_mb = 768
duration_s = 5
invocations = 300_000
arch = 'arm64'
gb_s = invocations * (memory_mb/1024) * duration_s
price = 0.0000133334 if arch == 'arm64' else 0.0000166667
free_gb_s = 400_000
free_requests = 1_000_000
compute = max(0, gb_s - free_gb_s) * price
requests = max(0, invocations - free_requests) * 0.20 / 1_000_000
print(f'GB-seconds: {gb_s:,.0f}')
print(f'Billable GB-s: {max(0, gb_s - free_gb_s):,.0f}')
print(f'Compute cost: \${compute:.2f}')
print(f'Request cost: \${requests:.4f}')
print(f'Total Lambda: \${compute + requests:.2f}')
"
Troubleshooting
Problem 1: "The bill is much higher than estimated"
# Diagnosis: check real vs estimated duration
aws logs filter-log-events \
--log-group-name /aws/lambda/ai-endpoint \
--filter-pattern "REPORT" \
--limit 50
# Look for Duration and Memory Size in the REPORT lines:
# REPORT Duration: 8234.56 ms Billed Duration: 8235 ms Memory Size: 768 MB
# If the real duration is higher than your estimate:
# 1. The LLM is responding slower (OpenAI has variability)
# 2. Cold starts are adding duration
# 3. SDK retries are doubling the duration
# Action: check the p99 of duration, not just the average
aws cloudwatch get-metric-statistics \
--namespace AWS/Lambda \
--metric-name Duration \
--dimensions Name=FunctionName,Value=ai-endpoint \
--start-time 2026-03-01 --end-time 2026-03-08 \
--period 86400 --statistics Average p99 Maximum
Problem 2: "CloudWatch Logs costs more than Lambda"
# If you log large JSON (LLM responses), the logs grow fast
# $0.50/GB ingestion + $0.03/GB/month storage
# Solutions:
# 1. Don't log the full content of responses
# 2. Use log level INFO in production (not DEBUG)
# 3. Configure log retention (don't keep logs forever)
aws logs put-retention-policy \
--log-group-name /aws/lambda/ai-endpoint \
--retention-in-days 14
# 4. Use structured logging with selective fields
# Log: tokens, duration, model, error
# Do NOT log: full prompt, full response
Problem 3: "Provisioned Concurrency doubles my bill"
# Provisioned Concurrency charges 24/7, not per invocation
# 10 instances × 768MB × 2,592,000 s/month = ~$83/month (just to have them)
# Diagnosis: do you really need Provisioned Concurrency?
# If 3-5s cold starts are acceptable → don't use it
# If you have <1000 inv/day → definitely don't use it
# If you need it, minimize:
# 1. Use scheduled scaling: more instances during peak hours
# 2. Reduce the provisioned memory (fewer GB = lower cost)
# 3. Use it on a single Lambda (the most critical), not on all of them
Problem 4: "I don't understand the difference between Duration and Billed Duration"
Duration: the real execution time of your code
Billed Duration: rounded to the ms (since Dec 2020)
Init Duration: cold start time (only the first invocation)
REPORT RequestId: abc-123
Duration: 3456.78 ms
Billed Duration: 3457 ms ← You pay for this
Memory Size: 768 MB ← What you configured
Max Memory Used: 180 MB ← What you actually used
Init Duration: 1234.56 ms ← Cold start (only if it was cold)
Init Duration is NOT billed separately — it's already included in Duration.
But when you calculate your timeout, remember that the cold start
eats part of your timeout window.
Hands-On Exercises
Exercise 1: Calculate the cost of your endpoint
Write a Python function that receives: invocations per day, memory in MB, average duration in seconds, and the percentage of invocations that are cold starts. Calculate the monthly cost broken down (compute, requests, API Gateway, estimated CloudWatch) and show the cost per invocation.
See solution
def calculate_monthly_cost(
invocations_per_day: int,
memory_mb: int,
avg_duration_s: float,
cold_start_pct: float = 5.0,
cold_start_overhead_s: float = 3.0,
architecture: str = "arm64",
api_gateway: bool = True,
):
monthly_inv = invocations_per_day * 30
cold_inv = int(monthly_inv * cold_start_pct / 100)
warm_inv = monthly_inv - cold_inv
warm_gb_s = warm_inv * (memory_mb / 1024) * avg_duration_s
cold_gb_s = cold_inv * (memory_mb / 1024) * (avg_duration_s + cold_start_overhead_s)
total_gb_s = warm_gb_s + cold_gb_s
price_gb_s = 0.0000133334 if architecture == "arm64" else 0.0000166667
free_gb_s = 400_000
free_requests = 1_000_000
billable_gb_s = max(0, total_gb_s - free_gb_s)
billable_requests = max(0, monthly_inv - free_requests)
compute_cost = billable_gb_s * price_gb_s
request_cost = billable_requests * 0.20 / 1_000_000
lambda_total = compute_cost + request_cost
gw_cost = monthly_inv * 1.00 / 1_000_000 if api_gateway else 0
cw_cost = max(0.50, monthly_inv * 0.001 / 1000)
total = lambda_total + gw_cost + cw_cost
per_inv = total / max(1, monthly_inv)
print(f"=== Cost Estimate ({invocations_per_day:,}/day, {memory_mb}MB, {avg_duration_s}s avg) ===")
print(f"Monthly invocations: {monthly_inv:,}")
print(f"Total GB-seconds: {total_gb_s:,.0f} (billable: {billable_gb_s:,.0f})")
print(f"")
print(f"Lambda compute: ${compute_cost:.2f}")
print(f"Lambda requests: ${request_cost:.4f}")
print(f"API Gateway: ${gw_cost:.2f}")
print(f"CloudWatch (est): ${cw_cost:.2f}")
print(f"────────────────────────────")
print(f"TOTAL: ${total:.2f}/month")
print(f"Per invocation: ${per_inv:.6f}")
return total
calculate_monthly_cost(1000, 768, 5.0)
calculate_monthly_cost(10000, 768, 5.0)
calculate_monthly_cost(100000, 1024, 6.0, cold_start_pct=2.0)
Exercise 2: Compare Lambda vs VPS for your case
Using the calculator from exercise 1, generate a table comparing Lambda arm64 vs a $25/month VPS for 5 traffic levels: 1K, 5K, 10K, 50K, and 100K invocations/day. Include the total cost (Lambda + API Gateway + CloudWatch) and mark the winner at each level with ✅.
See solution
def lambda_cost(inv_day, memory_mb=768, duration_s=5.0):
monthly = inv_day * 30
gb_s = monthly * (memory_mb / 1024) * duration_s
billable_gb_s = max(0, gb_s - 400_000)
billable_req = max(0, monthly - 1_000_000)
compute = billable_gb_s * 0.0000133334
requests = billable_req * 0.20 / 1_000_000
gw = monthly * 1.00 / 1_000_000
cw = max(0.50, monthly * 0.001 / 1000)
return compute + requests + gw + cw
vps_cost = 25.00
print(f"{'Inv/day':>10} {'Inv/month':>12} {'Lambda':>10} {'VPS':>10} {'Winner':>10}")
print("─" * 58)
for daily in [1_000, 5_000, 10_000, 50_000, 100_000]:
monthly = daily * 30
lc = lambda_cost(daily)
winner = "Lambda ✅" if lc < vps_cost else "VPS ✅"
print(f"{daily:>10,} {monthly:>12,} ${lc:>8.2f} ${vps_cost:>8.2f} {winner:>10}")
# Result:
# Inv/day Inv/month Lambda VPS Winner
# ──────────────────────────────────────────────────────────
# 1,000 30,000 $0.53 $25.00 Lambda ✅
# 5,000 150,000 $4.70 $25.00 Lambda ✅
# 10,000 300,000 $11.98 $25.00 Lambda ✅
# 50,000 1,500,000 $70.84 $25.00 VPS ✅
# 100,000 3,000,000 $148.20 $25.00 VPS ✅
Exercise 3: Estimate the caching impact
Your endpoint receives 10,000 invocations/day. After analyzing the logs, you discover that 35% are repeated prompts. Calculate: (a) the monthly OpenAI savings if you implement caching, (b) the DynamoDB on-demand cost for the cache (estimating 100K reads and 65K writes/month), (c) the caching ROI.
See solution
inv_per_day = 10_000
monthly_inv = inv_per_day * 30 # 300,000
cache_hit_rate = 0.35
avg_tokens_input = 100
avg_tokens_output = 300
# OpenAI cost WITHOUT cache
openai_no_cache = (
monthly_inv * avg_tokens_input * 0.00015 / 1000 +
monthly_inv * avg_tokens_output * 0.0006 / 1000
)
# OpenAI cost WITH cache
uncached_inv = int(monthly_inv * (1 - cache_hit_rate))
openai_with_cache = (
uncached_inv * avg_tokens_input * 0.00015 / 1000 +
uncached_inv * avg_tokens_output * 0.0006 / 1000
)
openai_savings = openai_no_cache - openai_with_cache
# DynamoDB on-demand
reads = 300_000 # Every invocation does a read
writes = int(monthly_inv * (1 - cache_hit_rate)) # Only misses write
dynamo_read_cost = reads * 0.25 / 1_000_000 # $0.25 per 1M RRU
dynamo_write_cost = writes * 1.25 / 1_000_000 # $1.25 per 1M WRU
dynamo_storage = 0.25 # ~1GB estimated
dynamo_total = dynamo_read_cost + dynamo_write_cost + dynamo_storage
net_savings = openai_savings - dynamo_total
roi = (net_savings / dynamo_total) * 100
print(f"=== Cache Impact Analysis ===")
print(f"Monthly invocations: {monthly_inv:,}")
print(f"Cache hit rate: {cache_hit_rate:.0%}")
print(f"")
print(f"OpenAI without cache: ${openai_no_cache:.2f}/month")
print(f"OpenAI with cache: ${openai_with_cache:.2f}/month")
print(f"OpenAI savings: ${openai_savings:.2f}/month")
print(f"")
print(f"DynamoDB cost: ${dynamo_total:.2f}/month")
print(f"Net savings: ${net_savings:.2f}/month")
print(f"ROI: {roi:.0f}%")
# === Cache Impact Analysis ===
# Monthly invocations: 300,000
# Cache hit rate: 35%
# OpenAI without cache: $58.50/month
# OpenAI with cache: $38.03/month
# OpenAI savings: $20.48/month
# DynamoDB cost: $0.57/month
# Net savings: $19.91/month
# ROI: 3493%
Exercise 4: Monthly budget with alerts
Using CloudWatch Billing Alarms and AWS Budgets, document (with AWS CLI code) how to configure: (a) an alert when Lambda exceeds $10/month, (b) an alert when the account's total spend exceeds $50/month, (c) a monthly budget with a notification at 80% of the limit.
See solution
# (a) Alert Lambda > $10/month
# First, enable billing alerts in your account
# AWS Console → Billing → Billing Preferences → Receive Billing Alerts
# Create an SNS topic for notifications
aws sns create-topic --name billing-alerts
aws sns subscribe \
--topic-arn arn:aws:sns:us-east-1:ACCOUNT:billing-alerts \
--protocol email \
--notification-endpoint you@email.com
# Create a CloudWatch Alarm for Lambda
aws cloudwatch put-metric-alarm \
--alarm-name "Lambda-Cost-Over-10" \
--alarm-description "Lambda monthly cost exceeds $10" \
--metric-name EstimatedCharges \
--namespace AWS/Billing \
--statistic Maximum \
--period 21600 \
--threshold 10 \
--comparison-operator GreaterThanThreshold \
--dimensions Name=ServiceName,Value=AWSLambda Name=Currency,Value=USD \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:ACCOUNT:billing-alerts
# (b) Alert total account > $50/month
aws cloudwatch put-metric-alarm \
--alarm-name "Total-Cost-Over-50" \
--alarm-description "Total monthly cost exceeds $50" \
--metric-name EstimatedCharges \
--namespace AWS/Billing \
--statistic Maximum \
--period 21600 \
--threshold 50 \
--comparison-operator GreaterThanThreshold \
--dimensions Name=Currency,Value=USD \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:ACCOUNT:billing-alerts
# (c) AWS Budget with notification at 80%
aws budgets create-budget \
--account-id ACCOUNT_ID \
--budget '{
"BudgetName": "Monthly-AI-Budget",
"BudgetLimit": {"Amount": "50", "Unit": "USD"},
"TimeUnit": "MONTHLY",
"BudgetType": "COST"
}' \
--notifications-with-subscribers '[
{
"Notification": {
"NotificationType": "ACTUAL",
"ComparisonOperator": "GREATER_THAN",
"Threshold": 80,
"ThresholdType": "PERCENTAGE"
},
"Subscribers": [
{"SubscriptionType": "EMAIL", "Address": "you@email.com"}
]
},
{
"Notification": {
"NotificationType": "ACTUAL",
"ComparisonOperator": "GREATER_THAN",
"Threshold": 100,
"ThresholdType": "PERCENTAGE"
},
"Subscribers": [
{"SubscriptionType": "EMAIL", "Address": "you@email.com"}
]
}
]'
Summary
- Lambda pricing = requests + GB-seconds. The free tier (1M requests + 400K GB-s) covers most learning projects and MVPs.
- For AI workloads, the OpenAI/Anthropic API cost is >90% of your bill at low/medium traffic. Lambda is almost free by comparison.
- The real cost includes: Lambda + API Gateway + CloudWatch + Secrets Manager + LLM API. Don't calculate Lambda alone.
- Lambda vs VPS break-even: ~600-700K invocations/month (with 768MB, 5s duration). Below that, Lambda wins. Above, a VPS is more economical.
- Optimize in this order: (1) cheaper LLM model, (2) response caching, (3) memory tuning, (4) arm64 architecture.
- Provisioned Concurrency is expensive. Only use it if cold starts are unacceptable for your business case.
- Configure billing alerts from day 1. A bug that generates infinite invocations can cost you hundreds of dollars in hours.
- Lambda shines with irregular traffic. You pay $0 when no one uses your endpoint. A VPS costs $25/month rain or shine.
Additional Resources
- Lambda Pricing — Updated prices and free tier
- AWS Pricing Calculator — Official calculator for estimates
- API Gateway Pricing — HTTP API vs REST API pricing
- CloudWatch Pricing — Logs, metrics, alarms
- OpenAI Pricing — Prices per model and per token
- AWS Budgets — Configure cost alerts
- Lambda Power Tuning — Optimize the cost/performance ratio
- Serverless Cost Calculator (community) — Alternative estimator for serverless