Lambda container images changed what is possible with serverless. The 250MB ZIP limit ruled out PyTorch, large ML models, and complex runtimes. Container images up to 10GB removed that constraint. But container cold starts work differently from ZIP because of ECR layer caching — and understanding this difference is the key to making containers fast.
⚡ TL;DR: Use containers when package exceeds 250MB, you need custom runtimes (Bun, Deno, Rust), or you have ML models. Use ZIP for small functions where cold start speed matters most. Container cold starts are not necessarily slower — ECR caches base image layers across Lambda, so only your application layer is downloaded.
ECR layer caching — why container cold starts can be fast
# Container image = stack of layers
# Lambda caches layers at the regional level
# Your image layers:
# Layer 1: public.ecr.aws/lambda/python:3.12 base (CACHED — Lambda pre-warms this)
# Layer 2: pip install dependencies (partially cached by digest)
# Layer 3: your application code (not cached — changes per deploy)
# Cold start = download uncached layers only
# If base = official AWS Lambda base image: Layer 1 pull ~0ms
# If deps layer is small: Layer 2 pull ~100ms
# Only Layer 3 (your code) is always downloaded
# Key: use official AWS Lambda base images
# From: public.ecr.aws/lambda/python:3.12
# NOT: python:3.12-slim (not pre-cached on Lambda infrastructure)
Optimized multi-stage Dockerfile
# Multi-stage: build stage has tools, final stage is minimal
FROM public.ecr.aws/lambda/python:3.12 AS builder
RUN pip install --upgrade pip
COPY requirements.txt .
RUN pip install --target /python -r requirements.txt
# Final stage — only Lambda runtime needs
FROM public.ecr.aws/lambda/python:3.12
COPY --from=builder /python ${LAMBDA_TASK_ROOT}
# Copy code LAST — changes most often, keeps deps layer cached
COPY handler.py ${LAMBDA_TASK_ROOT}/
CMD ["handler.lambda_handler"]
# Build for ARM64 (34% cheaper):
docker buildx build --platform linux/arm64 -t my-fn:arm64 .
# Image size impact:
# Naive single stage: 1.2GB
# Multi-stage optimized: 380MB
# Smaller = faster layer download = faster cold start
Cold start benchmarks: container vs ZIP
# Python 3.12, 512MB, us-east-1:
# Simple handler (hello world):
# ZIP: 180ms | Container: 620ms — ZIP wins for small functions
# With pandas + numpy (45MB deps):
# ZIP: 380ms | Container: 480ms — similar, ECR caches base
# PyTorch inference (800MB model — impossible with ZIP):
# Container: 3,200ms first cold start after deploy
# Container: 840ms after ECR cache warms
# Container + Provisioned Concurrency: 0ms
# Custom runtime — Bun (ultra-fast JS):
# Node.js 20 ZIP: 180ms
# Bun container: 95ms — container overhead outweighed by runtime speed
Container decision checklist
- ✅ Use container images for ML models, binaries over 250MB, custom runtimes
- ✅ Use
public.ecr.aws/lambda/base images — pre-cached on Lambda infrastructure - ✅ Multi-stage builds — no build tools in final image
- ✅ Copy application code as LAST layer — keeps deps layer cached across deploys
- ✅ Build for ARM64 — same 34% cost saving applies to container Lambda
- ❌ Do not use containers for small functions under 50MB — ZIP has faster cold starts
- ❌ Do not include dev dependencies or test files in final image
Container Lambda with ML models pairs directly with Lambda Power Tuning — ML inference typically hits the 1769MB vCPU sweet spot. For container cold start monitoring, CloudWatch Insights cold start queries work identically for container and ZIP functions. Official reference: Lambda container images documentation.
Master AWS Lambda
→ AWS Solutions Architect Course on Udemy — The most comprehensive AWS course covering Lambda, serverless patterns, and production architecture.
→ AWS Certified Solutions Architect Study Guide — Deep Lambda chapter covering cold starts, VPC, layers, and SnapStart.
Sponsored links. We may earn a commission at no extra cost to you.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
