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
| Aspect | Zip | Container |
|---|---|---|
| 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 size | 50MB zip / 250MB unzipped | 10GB |
| Required tools | pip, zip | Docker, ECR |
| CI/CD complexity | Low | Medium (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
| Scenario | Zip | Container |
|---|---|---|
| Only openai SDK (~5MB) | ✅ Ideal | Overkill |
| openai + anthropic (~12MB) | ✅ Works | Overkill |
| 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
| Aspect | Zip | Container |
|---|---|---|
| Local testing | SAM CLI (simulates Lambda) | Docker run (same as production) |
| Debugging | sam local invoke | docker run + logs |
| Local/prod consistency | Medium (pip may differ) | High (same container) |
| Hot reload | Not native | Volume mount in dev |
| Team familiarity | Low (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 app | Recommendation | Reason |
|---|---|---|
| API that invokes OpenAI/Anthropic | Zip | Light SDK, fast build |
| API with simple LangChain | Container | LangChain + deps ~80MB |
| RAG with local embeddings | Container | Needs numpy, possibly torch |
| AI image processing | Container | Pillow, possibly ffmpeg |
| Classifier with sklearn | Container | sklearn + 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 deploycommands for both modes. - The difference on warm starts is zero — the impact is only on cold starts.
Additional Resources
- Lambda Deployment Packages — Zip — Official zip packaging documentation
- Lambda Container Images — Container deployment documentation
- AWS ECR — Pushing Images — Pushing images to ECR
- Lambda Base Images — Official Lambda base images
- SAM Build Reference — SAM build for zip and container
- Docker Multi-stage Builds — Docker image optimization
- Lambda Package Size Limits — Official size limits