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

  1. Lambda Pricing — Updated prices and free tier
  2. AWS Pricing Calculator — Official calculator for estimates
  3. API Gateway Pricing — HTTP API vs REST API pricing
  4. CloudWatch Pricing — Logs, metrics, alarms
  5. OpenAI Pricing — Prices per model and per token
  6. AWS Budgets — Configure cost alerts
  7. Lambda Power Tuning — Optimize the cost/performance ratio
  8. Serverless Cost Calculator (community) — Alternative estimator for serverless