Module 3: Serverless & Lambda for AI

3. Container vs Zip Deployment

Overview

In this capsule you'll learn the two ways to deploy a Lambda function — zip package and container image — with a focus on AI workloads. It's not an abstract comparison: you'll deploy both ways with the same AI function and measure the differences in build time, cold start, and dependency management. By the end, you'll know exactly when to use each one for your case.

Context: In the previous capsule you wrote your first Lambda for AI with basic packaging. Here you go deep on the most important packaging decision: zip or container? For a function that only uses the OpenAI SDK (~5MB), zip is enough. For a function with LangChain, numpy, or compiled dependencies, container is the only viable option. But it's not just size — cold starts, build speed, and developer experience also change.


The Two Ways to Deploy Lambda

Quick overview

Zip Package:
├── Your code + dependencies in a .zip
├── You upload the .zip to Lambda directly
├── Limit: 50MB compressed / 250MB uncompressed
├── Cold start: faster (Lambda caches the zip)
└── Ideal for: light dependencies (openai, httpx)

Container Image:
├── Dockerfile based on a Lambda image
├── You build the image, push to ECR (registry)
├── Limit: 10GB image
├── Cold start: slower (image pull)
└── Ideal for: heavy dependencies (langchain, numpy, pandas)

The analogy

Think of the two options as two ways to deliver ingredients to a kitchen:

  • Zip is a grocery bag. Fast to prepare, easy to carry, but it has a weight limit. If you try to fit 50 ingredients, the bag breaks.
  • Container is a moving box. Bigger, heavier, takes longer to load, but everything you need fits. You can even put a refrigerator inside.

Zip Deployment: Step-by-Step

When to use zip

  • ✅ Total dependencies < 50MB compressed
  • ✅ Only API SDKs (openai, anthropic, httpx)
  • ✅ You don't need libraries with compiled binaries (numpy on Linux)
  • ✅ You want the fastest possible cold start
  • ✅ Fast deploy in development cycles

Step 1: Project structure

lambda-zip/
├── handler.py
├── requirements.txt
└── build.sh
# handler.py
import json
import os
import time
from openai import OpenAI

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

def handler(event, context):
    start = time.time()

    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 is required"})

    try:
        result = client.chat.completions.create(
            model=os.environ.get("MODEL_NAME", "gpt-4o-mini"),
            messages=[{"role": "user", "content": prompt}],
            max_tokens=int(os.environ.get("MAX_TOKENS", "500")),
        )
    except Exception as e:
        return _response(502, {"error": f"LLM error: {str(e)}"})

    return _response(200, {
        "answer": result.choices[0].message.content,
        "tokens": result.usage.total_tokens,
        "duration_ms": int((time.time() - start) * 1000),
    })


def _response(status, body):
    return {
        "statusCode": status,
        "headers": {
            "Content-Type": "application/json",
            "Access-Control-Allow-Origin": "*",
        },
        "body": json.dumps(body),
    }
# requirements.txt
openai>=1.0.0

Step 2: Package the dependencies

#!/bin/bash
# build.sh — Packages a Lambda zip for deployment

set -e

PACKAGE_DIR="package"
ZIP_FILE="deployment.zip"

rm -rf $PACKAGE_DIR $ZIP_FILE

# Install dependencies into a temporary directory
# --platform manylinux2014_x86_64 ensures Lambda-compatible binaries
pip install \
    -r requirements.txt \
    -t $PACKAGE_DIR \
    --platform manylinux2014_x86_64 \
    --only-binary=:all: \
    --python-version 3.11

# Create a zip with the dependencies
cd $PACKAGE_DIR
zip -r ../$ZIP_FILE . -x '*.pyc' '__pycache__/*'
cd ..

# Add the handler to the zip
zip $ZIP_FILE handler.py

# Check the size
echo "Package size:"
ls -lh $ZIP_FILE
unzip -l $ZIP_FILE | tail -1

echo "Done. Deploy with:"
echo "aws lambda update-function-code --function-name ai-endpoint --zip-file fileb://$ZIP_FILE"
# Run it
chmod +x build.sh
./build.sh

# Expected output:
# Package size:
# -rw-r--r-- 1 user staff 5.2M deployment.zip
# 142 files, 18234567 bytes uncompressed

Step 3: Deploy

# First time: create the function
aws lambda create-function \
    --function-name ai-endpoint-zip \
    --runtime python3.11 \
    --handler handler.handler \
    --zip-file fileb://deployment.zip \
    --role arn:aws:iam::123456789:role/lambda-role \
    --timeout 60 \
    --memory-size 512 \
    --environment "Variables={OPENAI_API_KEY=sk-xxx,MODEL_NAME=gpt-4o-mini}"

# Update existing code
aws lambda update-function-code \
    --function-name ai-endpoint-zip \
    --zip-file fileb://deployment.zip

Step 4: Verify

# Test invocation
aws lambda invoke \
    --function-name ai-endpoint-zip \
    --payload '{"body": "{\"prompt\": \"What is Lambda?\"}"}' \
    output.json

cat output.json
# {"statusCode": 200, "body": "{\"answer\":\"...\",\"tokens\":42,\"duration_ms\":2100}"}

Container Deployment: Step-by-Step

When to use container

  • ✅ Total dependencies > 50MB (langchain, numpy, pandas)
  • ✅ You need libraries with compiled binaries
  • ✅ You want local build = Lambda build consistency
  • ✅ Your team already works with Docker (guide #15)
  • ✅ You need system binaries (ffmpeg, imagemagick)

Step 1: Project structure

lambda-container/
├── handler.py
├── requirements.txt
└── Dockerfile
# handler.py — Same code as zip, with extra dependencies
import json
import os
import time
from openai import OpenAI
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

# LangChain chain initialized outside the handler
prompt_template = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful AI assistant. Be concise."),
    ("user", "{input}"),
])

chain = prompt_template | ChatOpenAI(
    model="gpt-4o-mini",
    api_key=os.environ["OPENAI_API_KEY"],
    max_tokens=500,
)


def handler(event, context):
    start = time.time()

    try:
        body = json.loads(event.get("body", "{}"))
    except json.JSONDecodeError:
        return _response(400, {"error": "Invalid JSON"})

    prompt = body.get("prompt", "").strip()
    use_chain = body.get("use_chain", False)

    if not prompt:
        return _response(400, {"error": "prompt is required"})

    try:
        if use_chain:
            result = chain.invoke({"input": prompt})
            answer = result.content
            tokens = None
        else:
            result = client.chat.completions.create(
                model="gpt-4o-mini",
                messages=[{"role": "user", "content": prompt}],
                max_tokens=500,
            )
            answer = result.choices[0].message.content
            tokens = result.usage.total_tokens
    except Exception as e:
        return _response(502, {"error": f"AI error: {str(e)}"})

    return _response(200, {
        "answer": answer,
        "tokens": tokens,
        "used_chain": use_chain,
        "duration_ms": int((time.time() - start) * 1000),
    })


def _response(status, body):
    return {
        "statusCode": status,
        "headers": {
            "Content-Type": "application/json",
            "Access-Control-Allow-Origin": "*",
        },
        "body": json.dumps(body),
    }
# requirements.txt
openai>=1.0.0
langchain>=0.2.0
langchain-openai>=0.1.0
langchain-core>=0.2.0

Step 2: Dockerfile

# Dockerfile
# Official AWS Lambda base image for Python
FROM public.ecr.aws/lambda/python:3.11

# Install dependencies first (Docker layer caching)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy code
COPY handler.py .

# Handler entrypoint
CMD ["handler.handler"]

Step 3: Build and local test

# Build
docker build -t ai-endpoint-lambda .

# Check the image size
docker images ai-endpoint-lambda
# REPOSITORY            TAG     SIZE
# ai-endpoint-lambda    latest  ~650MB (langchain + deps)

# Local test — the Lambda Runtime Interface Emulator is included in the base image
docker run -p 9000:8080 \
    -e OPENAI_API_KEY=sk-xxx \
    ai-endpoint-lambda

# In another terminal:
curl -X POST "http://localhost:9000/2015-03-31/functions/function/invocations" \
    -d '{"body": "{\"prompt\": \"What is Lambda?\"}"}'

Step 4: Push to ECR and deploy

# Create a repository in ECR
aws ecr create-repository --repository-name ai-endpoint-lambda

# Log in to ECR
aws ecr get-login-password --region us-east-1 | \
    docker login --username AWS --password-stdin \
    123456789.dkr.ecr.us-east-1.amazonaws.com

# Tag and push
docker tag ai-endpoint-lambda:latest \
    123456789.dkr.ecr.us-east-1.amazonaws.com/ai-endpoint-lambda:latest

docker push \
    123456789.dkr.ecr.us-east-1.amazonaws.com/ai-endpoint-lambda:latest

# Create the Lambda function from the container
aws lambda create-function \
    --function-name ai-endpoint-container \
    --package-type Image \
    --code ImageUri=123456789.dkr.ecr.us-east-1.amazonaws.com/ai-endpoint-lambda:latest \
    --role arn:aws:iam::123456789:role/lambda-role \
    --timeout 60 \
    --memory-size 512 \
    --environment "Variables={OPENAI_API_KEY=sk-xxx}"

Step 5: Verify

aws lambda invoke \
    --function-name ai-endpoint-container \
    --payload '{"body": "{\"prompt\": \"What is Lambda?\"}"}' \
    output.json

cat output.json

Detailed Comparison

Build and deployment

AspectZipContainer
Build time (first time)~10s~60-120s
Build time (incremental)~5s~10-20s (Docker cache)
Deploy time~5s (upload zip)~30-60s (push image)
Max size50MB zip / 250MB unzipped10GB
Required toolspip, zipDocker, ECR
CI/CD complexityLowMedium (build + push)

Cold start

Measured cold start (same function, different packaging):

Zip (openai ~5MB):
├── Init:      ~800ms
├── Handler:   ~2500ms (LLM call)
└── Total:     ~3300ms

Container (openai ~5MB, same function):
├── Image pull: ~1200ms
├── Init:       ~900ms
├── Handler:    ~2500ms (LLM call)
└── Total:      ~4600ms

Container (langchain ~100MB):
├── Image pull: ~3500ms
├── Init:       ~2000ms
├── Handler:    ~2500ms (LLM call)
└── Total:      ~8000ms

The container overhead is ~1-2s extra on cold start due to the image pull. For warm starts, the difference disappears.

Dependency management

ScenarioZipContainer
Only openai SDK (~5MB)✅ IdealOverkill
openai + anthropic (~12MB)✅ WorksOverkill
langchain + deps (~80MB)⚠️ Tight (250MB limit)✅ Ideal
numpy + pandas (~120MB)❌ Exceeds the limit✅ Works
torch (~800MB)❌ Impossible⚠️ Works but long cold start
System binaries (ffmpeg)❌ Not supported✅ You can install whatever you want

Developer experience

AspectZipContainer
Local testingSAM CLI (simulates Lambda)Docker run (same as production)
Debuggingsam local invokedocker run + logs
Local/prod consistencyMedium (pip may differ)High (same container)
Hot reloadNot nativeVolume mount in dev
Team familiarityLow (Lambda-specific)High (Docker, guide #15)

Decision Tree: Zip or Container?

Do your total dependencies (unzipped) exceed 250MB?
├── YES → Container
│         (numpy+pandas, langchain+chromadb, torch)
└── NO → Do you need system binaries (ffmpeg, etc.)?
          ├── YES → Container
          └── NO → Does your team already use Docker for everything?
                    ├── YES → Container (consistency)
                    └── NO → Do you need to minimize cold start?
                              ├── YES → Zip (1-2s less on cold start)
                              └── NO → Zip (simpler)

Recommendation for common AI cases

Your AI appRecommendationReason
API that invokes OpenAI/AnthropicZipLight SDK, fast build
API with simple LangChainContainerLangChain + deps ~80MB
RAG with local embeddingsContainerNeeds numpy, possibly torch
AI image processingContainerPillow, possibly ffmpeg
Classifier with sklearnContainersklearn + numpy > 100MB

SAM Template for Both Modes

Zip deployment with SAM

# template-zip.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  AIEndpointZip:
    Type: AWS::Serverless::Function
    Properties:
      Handler: handler.handler
      Runtime: python3.11
      CodeUri: lambda-zip/
      Timeout: 60
      MemorySize: 512
      Environment:
        Variables:
          OPENAI_API_KEY: !Ref OpenAIKey
      Events:
        Ask:
          Type: Api
          Properties:
            Path: /ask
            Method: post

Parameters:
  OpenAIKey:
    Type: String
    NoEcho: true

Container deployment with SAM

# template-container.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  AIEndpointContainer:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
      Timeout: 60
      MemorySize: 512
      Environment:
        Variables:
          OPENAI_API_KEY: !Ref OpenAIKey
      Events:
        Ask:
          Type: Api
          Properties:
            Path: /ask
            Method: post
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: lambda-container/
      DockerTag: latest

Parameters:
  OpenAIKey:
    Type: String
    NoEcho: true
# Deploy zip
sam build -t template-zip.yaml
sam deploy --guided

# Deploy container
sam build -t template-container.yaml
sam deploy --guided

Package Size Optimization

For zip: minimize dependencies

# Exclude tests, docs, and unnecessary files
pip install openai -t package/ --no-deps
pip install httpx anyio certifi -t package/

# Exclude when packaging
cd package
zip -r ../deployment.zip . \
    -x '*.pyc' \
    -x '__pycache__/*' \
    -x '*.dist-info/*' \
    -x 'tests/*' \
    -x '*.md' \
    -x '*.txt'

For container: multi-stage builds

# Optimized Dockerfile — multi-stage
FROM public.ecr.aws/lambda/python:3.11 AS builder

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Remove unnecessary files to reduce the image
RUN find /var/lang/lib/python3.11/site-packages -type d -name "tests" -exec rm -rf {} + 2>/dev/null; \
    find /var/lang/lib/python3.11/site-packages -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null; \
    find /var/lang/lib/python3.11/site-packages -name "*.pyc" -delete 2>/dev/null; \
    exit 0

FROM public.ecr.aws/lambda/python:3.11

COPY --from=builder /var/lang/lib/python3.11/site-packages /var/lang/lib/python3.11/site-packages
COPY handler.py .

CMD ["handler.handler"]
# Compare sizes
docker build -t ai-lambda-optimized -f Dockerfile.optimized .
docker build -t ai-lambda-standard -f Dockerfile .

docker images | grep ai-lambda
# ai-lambda-standard    latest   650MB
# ai-lambda-optimized   latest   520MB
# ~20% reduction = faster cold start

Troubleshooting

Problem 1: "Zip file exceeds the 50 MB limit"

# Check what takes up the most space
du -sh package/* | sort -hr | head -10
# 25M package/numpy
# 18M package/pandas
# ...

# Options:
# 1. Use a container instead of zip
# 2. Move large dependencies to layers
# 3. Remove unnecessary dependencies

Problem 2: "Unable to import module 'handler': No module named 'X'"

# In zip: check the zip structure
unzip -l deployment.zip | head -20
# handler.py must be at the root
# Dependencies must be at the root (not in a subdirectory)

# Wrong:
# deployment.zip/
#   package/
#     openai/
#   handler.py

# Right:
# deployment.zip/
#   openai/
#   handler.py

Problem 3: "Container won't start — exec format error"

# You built for ARM (Mac M1/M2) but Lambda uses x86_64
docker build --platform linux/amd64 -t ai-lambda .

# Or in docker buildx
docker buildx build --platform linux/amd64 -t ai-lambda .

Problem 4: "Container image not found in ECR"

# Verify that you pushed to the right repo
aws ecr describe-images \
    --repository-name ai-endpoint-lambda

# Verify that the URI in the Lambda function matches
aws lambda get-function \
    --function-name ai-endpoint-container \
    --query 'Code.ImageUri'

Problem 5: "Container build takes too long in CI/CD"

# Use Docker layer caching in CI
# In GitHub Actions:
# - name: Build with cache
#   uses: docker/build-push-action@v5
#   with:
#     cache-from: type=gha
#     cache-to: type=gha,mode=max

# Order the Dockerfile: deps first, code after
# COPY requirements.txt . ← changes rarely
# RUN pip install ...      ← gets cached
# COPY handler.py .        ← changes often

Hands-On Exercises

Exercise 1: Complete zip deploy

Create a zip deployment of the AI handler function (the one from capsule 02) with the OpenAI SDK. Verify the package size and compare it to the 50MB limit. Invoke it locally with SAM.

See solution
# Structure
mkdir -p lambda-zip-exercise
cd lambda-zip-exercise

# handler.py (from the previous exercise)
cat > handler.py << 'HANDLER'
import json
import os
from openai import OpenAI

client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "test"))

def handler(event, context):
    try:
        body = json.loads(event.get("body", "{}"))
    except json.JSONDecodeError:
        return {"statusCode": 400, "body": json.dumps({"error": "Invalid JSON"})}

    prompt = body.get("prompt", "")
    if not prompt:
        return {"statusCode": 400, "body": json.dumps({"error": "prompt required"})}

    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        max_tokens=500,
    )

    return {
        "statusCode": 200,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps({"answer": response.choices[0].message.content}),
    }
HANDLER

echo "openai>=1.0.0" > requirements.txt

# Build
mkdir package
pip install -r requirements.txt -t package/ \
    --platform manylinux2014_x86_64 \
    --only-binary=:all: \
    --python-version 3.11

cd package && zip -r ../deployment.zip . && cd ..
zip deployment.zip handler.py

# Verify
ls -lh deployment.zip
# ~5MB — well within the 50MB limit

echo "Ratio: $(ls -l deployment.zip | awk '{print $5}') bytes / 52428800 bytes limit"
echo "$(echo "scale=1; $(ls -l deployment.zip | awk '{print $5}') * 100 / 52428800" | bc)% of the limit"

Exercise 2: Complete container deploy

Create a container deployment with LangChain. Build the image, verify the size, and test it locally with Docker.

See solution
mkdir -p lambda-container-exercise
cd lambda-container-exercise

# requirements.txt with langchain
cat > requirements.txt << 'EOF'
openai>=1.0.0
langchain>=0.2.0
langchain-openai>=0.1.0
EOF

# handler.py
cat > handler.py << 'HANDLER'
import json
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

llm = ChatOpenAI(
    model="gpt-4o-mini",
    api_key=os.environ.get("OPENAI_API_KEY", "test"),
)

prompt = ChatPromptTemplate.from_messages([
    ("system", "Respond concisely in one paragraph."),
    ("user", "{input}"),
])

chain = prompt | llm

def handler(event, context):
    try:
        body = json.loads(event.get("body", "{}"))
    except json.JSONDecodeError:
        return {"statusCode": 400, "body": json.dumps({"error": "Invalid JSON"})}

    user_input = body.get("prompt", "")
    if not user_input:
        return {"statusCode": 400, "body": json.dumps({"error": "prompt required"})}

    result = chain.invoke({"input": user_input})

    return {
        "statusCode": 200,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps({"answer": result.content}),
    }
HANDLER

# Dockerfile
cat > Dockerfile << 'DOCKER'
FROM public.ecr.aws/lambda/python:3.11
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY handler.py .
CMD ["handler.handler"]
DOCKER

# Build (force x86 platform for Lambda compatibility)
docker build --platform linux/amd64 -t ai-lambda-langchain .

# Verify the size
docker images ai-lambda-langchain --format "{{.Size}}"
# ~600-700MB

# Local test
docker run -d -p 9000:8080 \
    -e OPENAI_API_KEY=$OPENAI_API_KEY \
    --name lambda-test \
    ai-lambda-langchain

curl -s -X POST "http://localhost:9000/2015-03-31/functions/function/invocations" \
    -d '{"body": "{\"prompt\": \"What is LangChain?\"}"}'

docker stop lambda-test && docker rm lambda-test

Exercise 3: Measure the cold start difference

Deploy the same function (only openai SDK) in both formats and measure each one's cold start. Document the results.

See solution
# Method: use SAM local to simulate cold starts
# SAM local always simulates a cold start (creates a new container)

# Zip
cd lambda-zip-exercise

cat > template.yaml << 'TEMPLATE'
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  TestFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: handler.handler
      Runtime: python3.11
      CodeUri: .
      Timeout: 60
      MemorySize: 512
TEMPLATE

cat > env.json << 'ENV'
{"TestFunction": {"OPENAI_API_KEY": "sk-test"}}
ENV

# Measure time (without a real OpenAI call — measures only init)
time sam local invoke TestFunction \
    --event '{"body": "{\"prompt\": \"test\"}"}' \
    --env-vars env.json \
    --skip-pull-image 2>&1 | tail -5

# Container
cd ../lambda-container-exercise

cat > template.yaml << 'TEMPLATE'
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  TestFunction:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
      Timeout: 60
      MemorySize: 512
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: .
TEMPLATE

time sam local invoke TestFunction \
    --event '{"body": "{\"prompt\": \"test\"}"}' \
    --env-vars env.json 2>&1 | tail -5

# Document the results:
# Zip (openai only):    Init ~Xs, Total ~Xs
# Container (openai):   Init ~Xs, Total ~Xs
# Difference:           ~X seconds

The typical difference is 1-2 seconds of additional cold start for container vs zip with the same dependencies. On warm starts, the difference is practically zero.

Exercise 4: Optimize the Dockerfile with multi-stage

Take the Dockerfile from exercise 2 (with LangChain) and create an optimized version with a multi-stage build that removes unnecessary files. Compare the image sizes.

See solution
# Dockerfile.optimized
FROM public.ecr.aws/lambda/python:3.11 AS builder

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Aggressive cleanup of files not needed at runtime
RUN find /var/lang/lib/python3.11/site-packages -type d -name "tests" -exec rm -rf {} + 2>/dev/null; \
    find /var/lang/lib/python3.11/site-packages -type d -name "test" -exec rm -rf {} + 2>/dev/null; \
    find /var/lang/lib/python3.11/site-packages -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null; \
    find /var/lang/lib/python3.11/site-packages -type d -name "docs" -exec rm -rf {} + 2>/dev/null; \
    find /var/lang/lib/python3.11/site-packages -name "*.pyc" -delete 2>/dev/null; \
    find /var/lang/lib/python3.11/site-packages -name "*.pyo" -delete 2>/dev/null; \
    find /var/lang/lib/python3.11/site-packages -name "*.md" -delete 2>/dev/null; \
    exit 0

FROM public.ecr.aws/lambda/python:3.11

COPY --from=builder /var/lang/lib/python3.11/site-packages /var/lang/lib/python3.11/site-packages
COPY handler.py .

CMD ["handler.handler"]
# Build both versions
docker build --platform linux/amd64 -t ai-lambda-standard -f Dockerfile .
docker build --platform linux/amd64 -t ai-lambda-optimized -f Dockerfile.optimized .

# Compare
docker images --format "table {{.Repository}}\t{{.Size}}" | grep ai-lambda
# ai-lambda-standard    ~650MB
# ai-lambda-optimized   ~520MB
# Savings: ~130MB (~20%)

echo "Reduction: $(echo "scale=1; (650-520)*100/650" | bc)%"

A 20% reduction in image size translates directly into faster cold starts because Lambda needs less time to pull the image.


Summary

  • Zip deployment is simpler and produces faster cold starts, but has a 50MB/250MB limit. Ideal for light SDKs (openai, anthropic).
  • Container deployment supports up to 10GB and any dependency, but adds 1-2s of cold start due to the image pull. Necessary for langchain, numpy, pandas.
  • For AI workloads, the decision depends on the size of your dependencies: if they fit in a zip, use it. If not, container.
  • Always optimize: exclude tests/docs in zip, multi-stage builds in container. Less size = less cold start.
  • SAM CLI unifies development: same sam build, sam deploy commands for both modes.
  • The difference on warm starts is zero — the impact is only on cold starts.

Additional Resources

  1. Lambda Deployment Packages — Zip — Official zip packaging documentation
  2. Lambda Container Images — Container deployment documentation
  3. AWS ECR — Pushing Images — Pushing images to ECR
  4. Lambda Base Images — Official Lambda base images
  5. SAM Build Reference — SAM build for zip and container
  6. Docker Multi-stage Builds — Docker image optimization
  7. Lambda Package Size Limits — Official size limits