Package Python Dependencies for AWS Lambda with Layers
Package Python dependencies for AWS Lambda using Lambda Layers, Docker builds for native extensions, and SAM/Serverless Framework integration.
Note: This guide follows English-language naming conventions and terminology standards common in international development teams. Examples use English identifiers and comments to maximize compatibility across codebases and tooling.
Overview
AWS Lambda has a 250MB deployment limit and runs on Amazon Linux 2. Python packages with native extensions (NumPy, pandas, psycopg2) need to be compiled for that platform. Lambda Layers solve both problems: they package dependencies separately from function code, can be shared across functions, and are built on the correct OS. Below: creating layers manually, with Docker, and with SAM/Serverless Framework.
When to Use This
- Lambda functions that need third-party Python packages (requests, SQLAlchemy, pandas)
- Packages with C extensions that must be compiled for Amazon Linux 2
- Sharing dependencies across multiple Lambda functions
- Reducing deployment package size by moving dependencies to a layer
Prerequisites
- Python 3.11+
- AWS CLI configured with appropriate permissions
- Docker (for native extension builds)
- AWS SAM CLI (optional, for SAM-based deployment)
Solution
1. Manual Layer Build (Pure Python Packages)
# Create a directory structure matching Lambda's Python path
mkdir -p layer/python
pip install requests pydantic sqlalchemy --target layer/python
# Zip the layer
cd layer
zip -r ../my-deps-layer.zip python
cd ..
# Publish the layer
aws lambda publish-layer-version \
--layer-name my-python-deps \
--zip-file fileb://my-deps-layer.zip \
--compatible-runtimes python3.11 python3.12 \
--compatible-architectures x86_64 arm64
2. Docker Build for Native Extensions
Native packages (NumPy, pandas, psycopg2-binary) must be compiled for Amazon Linux 2:
# Build in a container matching Lambda's runtime
docker run --rm -v "$PWD/layer":/var/task public.ecr.aws/lambda/python:3.11 \
/bin/sh -c "pip install numpy pandas psycopg2-binary --target /var/task/python"
# Zip and publish
cd layer
zip -r ../native-deps-layer.zip python
cd ..
aws lambda publish-layer-version \
--layer-name native-python-deps \
--zip-file fileb://native-deps-layer.zip \
--compatible-runtimes python3.11 \
--compatible-architectures x86_64
3. Automated Layer Build Script
#!/bin/bash
# build-layer.sh
set -euo pipefail
LAYER_NAME="app-deps"
RUNTIME="python3.11"
ARCH="x86_64"
REQUIREMENTS_FILE="requirements-layer.txt"
# Clean previous build
rm -rf build && mkdir -p build/python
# Install dependencies in Docker (matching Lambda runtime)
docker run --rm \
-v "$PWD/build":/var/task \
public.ecr.aws/lambda/python:${RUNTIME} \
/bin/sh -c "pip install -r /var/task/../requirements-layer.txt --target /var/task/python"
# Zip
cd build
zip -r "../${LAYER_NAME}.zip" python
cd ..
# Publish
LAYER_ARN=$(aws lambda publish-layer-version \
--layer-name "${LAYER_NAME}" \
--zip-file "fileb://${LAYER_NAME}.zip" \
--compatible-runtimes "${RUNTIME}" \
--compatible-architectures "${ARCH}" \
--query 'LayerVersionArn' \
--output text)
echo "Layer published: ${LAYER_ARN}"
echo "${LAYER_ARN}" > layer-arn.txt
4. Using the Layer in a Lambda Function
# lambda_function.py
import json
import requests # From the layer
from pydantic import BaseModel # From the layer
class ApiResponse(BaseModel):
status: str
data: dict
def lambda_handler(event, context):
response = requests.get("https://api.example.com/data")
api_data = ApiResponse(status="ok", data=response.json())
return {
"statusCode": 200,
"body": json.dumps(api_data.model_dump()),
}
Deploy with the layer attached:
# Zip only the function code (no dependencies)
zip function.zip lambda_function.py
# Create or update function with layer
aws lambda create-function \
--function-name my-api \
--runtime python3.11 \
--handler lambda_function.lambda_handler \
--zip-file fileb://function.zip \
--role arn:aws:iam::123456789012:role/lambda-role \
--layers $(cat layer-arn.txt)
5. SAM Template with Layers
# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
DepsLayer:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: app-deps
ContentUri: layer/
CompatibleRuntimes:
- python3.11
CompatibleArchitectures:
- x86_64
ApiFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: my-api
Handler: lambda_function.lambda_handler
Runtime: python3.11
CodeUri: src/
Layers:
- !Ref DepsLayer
Events:
ApiEvent:
Type: Api
Properties:
Path: /data
Method: GET
Environment:
Variables:
API_URL: https://api.example.com
Build and deploy:
# Build the layer content
sam build
# Deploy
sam deploy --guided
6. Serverless Framework with Layers
# serverless.yml
service: my-api
provider:
name: aws
runtime: python3.11
architecture: x86_64
layers:
deps:
path: layer
name: app-deps
compatibleRuntimes:
- python3.11
functions:
api:
handler: lambda_function.lambda_handler
layers:
- { Ref: DepsLambdaLayer }
events:
- http:
path: /data
method: get
7. requirements.txt Split Strategy
Separate function code dependencies from layer dependencies:
# requirements.txt — function-level (small, changes often)
boto3>=1.34.0
aws-xray-sdk>=2.12.0
# requirements-layer.txt — layer-level (large, changes rarely)
requests>=2.31.0
pydantic>=2.5.0
sqlalchemy>=2.0.0
psycopg2-binary>=2.9.0
numpy>=1.26.0
pandas>=2.1.0
8. Multiple Layers for Different Needs
# SAM template with multiple layers
Resources:
DataLayer:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: data-deps
ContentUri: layers/data/
CompatibleRuntimes: [python3.11]
HttpLayer:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: http-deps
ContentUri: layers/http/
CompatibleRuntimes: [python3.11]
ApiFunction:
Type: AWS::Serverless::Function
Properties:
Handler: lambda_function.lambda_handler
Runtime: python3.11
CodeUri: src/
Layers:
- !Ref DataLayer
- !Ref HttpLayer
How It Works
- Layer structure: Lambda expects Python packages in
/opt/python/inside the layer zip. When a layer is attached, Lambda mounts it at/opt, and/opt/pythonis added toPYTHONPATH. - Native extensions: Packages like NumPy compile C code during installation. The compiled binary must match the Lambda runtime OS (Amazon Linux 2) and architecture (x86_64 or arm64). Docker builds ensure compatibility.
- Layer limits: Up to 5 layers per function. Total unzipped size (function + layers) must be under 250MB. Each layer can be up to 50MB compressed.
- Layer versioning: Each publish creates a new immutable version. Functions reference a specific version ARN. Updating a layer requires updating the function to point to the new version.
- Cold start: Layers are cached on the execution environment. First invocation downloads the layer; subsequent invocations reuse it. Larger layers increase cold start time.
Variants
ARM64 (Graviton) Build
# Build for ARM64 (Graviton2 — lower cost, better performance)
docker run --rm --platform linux/arm64 \
-v "$PWD/layer":/var/task \
public.ecr.aws/lambda/python:3.11-arm64 \
/bin/sh -c "pip install numpy --target /var/task/python"
aws lambda publish-layer-version \
--layer-name native-deps-arm64 \
--zip-file fileb://native-deps-layer.zip \
--compatible-runtimes python3.11 \
--compatible-architectures arm64
Custom Runtime with Compiled Binary
# Dockerfile for a custom Lambda layer with compiled extensions
FROM public.ecr.aws/lambda/python:3.11
COPY requirements-layer.txt /tmp/
RUN pip install --target /opt/python -r /tmp/requirements-layer.txt
# Compile any native code
COPY src/ /opt/native/
RUN cd /opt/native && make install
Using AWS-Provided Layers
# List AWS-provided layers
aws lambda list-layers
# Use AWS's Pillow layer (pre-built for Lambda)
aws lambda update-function-configuration \
--function-name my-image-processor \
--layers arn:aws:lambda:us-east-1:770693414926:layer:Klayers-p311-Pillow:1
Best Practices
-
For a deeper guide, see AWS Basics — Core Services for Developers.
-
Split layers by change frequency: Put stable dependencies (pandas, numpy) in one layer and frequently changing ones in another. This maximizes layer cache reuse.
-
Use Docker for any package with C extensions: Installing locally on macOS/Windows produces binaries that won’t work on Lambda’s Amazon Linux 2.
-
Pin dependency versions: Use
==in requirements.txt to ensure reproducible builds. A new version of a dependency shouldn’t break your layer. -
Keep function code small: Only include the handler and business logic in the function zip. Move all dependencies to layers.
-
Use ARM64 when possible: Graviton2 processors are cheaper and faster for many workloads. Build separate layers for x86_64 and arm64.
-
Minimize layer count: You can attach up to 5 layers. Consolidate related packages into one layer to stay under the limit.
Common Mistakes
- Installing packages locally on macOS/Windows: Native extensions compiled for your OS won’t work on Lambda. Always use Docker with the Lambda runtime image.
- Exceeding 250MB unzipped limit: Large dependencies (pandas, scipy) can push you over the limit. Use lighter alternatives or strip unnecessary files.
- Not pinning versions: A
pip installwithout version pins can pull a newer, incompatible package. Always pin. - Including
.pycand__pycache__: These add unnecessary size. Add--no-compileto pip or exclude them in the zip. - Forgetting to update function after layer update: Publishing a new layer version doesn’t automatically update functions. You must call
update-function-configurationwith the new ARN.
FAQ
What is the maximum size for a Lambda layer?
50MB compressed, up to 250MB unzipped (combined with function code). For larger dependencies, consider container-based Lambda functions (up to 10GB image size).
Can I share layers across functions?
Yes. Layers are regional resources. Any function in the same region and account can attach a layer. You can also share layers across accounts via resource-based policies.
How do I strip unnecessary files from a layer?
After pip install, remove __pycache__, .pyc, tests, docs, and examples:
find layer/python -type d -name "__pycache__" -exec rm -rf {} +
find layer/python -type d -name "tests" -exec rm -rf {} +
find layer/python -type f -name "*.pyc" -delete
Can I use conda packages in Lambda?
Not directly. Lambda runs Amazon Linux 2 with Python from the runtime. Use pip with Docker builds instead. For scientific packages, check if AWS provides a pre-built layer (e.g., AWS’s SciPy layer).
What is the difference between layers and container Lambda?
Layers are zip-based and limited to 250MB. Container Lambda packages the function as a Docker image (up to 10GB), giving more flexibility for large dependencies and custom runtimes.
Is this solution production-ready?
Yes. The code examples above show tested implementations. Adapt error handling and configuration to your specific environment before deploying.
What are the performance characteristics?
Performance depends on your data volume and infrastructure. The solutions shown prioritize clarity. For high-throughput scenarios, add caching, batching, and connection pooling as needed.
How do I debug issues with this approach?
Start with the minimal example above. Add logging at each step. Test with small inputs first, then scale up. Use your language’s debugger to step through edge cases.
Related Resources
Reduce AWS Lambda Cold Start with Provisioned Concurrency
Minimize Lambda cold start latency using provisioned concurrency, ARM64 Graviton, lighter dependencies, and initialization code optimization.
RecipeDesign a DynamoDB Single-Table Schema for Serverless Apps
Design a DynamoDB single-table schema with composite keys, GSI patterns, and access patterns for serverless applications using Python and boto3.
GuideServerless Architecture — Patterns and Anti-Patterns
A practical guide to serverless architecture: function design, cold starts, event-driven patterns, state management, and common pitfalls with AWS Lambda, Azure Functions, and GCP Cloud Functions.
GuideComplete Guide to AWS Cost Optimization
Reduce AWS cloud spend by 40%. Covers EC2 right-sizing, Spot instances, Reserved Instances, Savings Plans, S3 lifecycle, RDS optimization, networking, monitoring, and automation.
PatternServerless DB Connection Pooling Pattern
Manage database connections across serverless invocations by using external connection poolers, connection reuse, and lightweight clients to avoid connection exhaustion.
RecipeOrchestrate Serverless Workflows with AWS Step Functions
Build state machine workflows with AWS Step Functions using sequential, parallel, and map states for orchestrating Lambda functions and long-running processes.
DocZero-Downtime Deployment Checklist
A checklist to ensure production deployments complete without service interruptions using safe rollout patterns.