AWS Lambda Layers: Organize Code and Dependencies
Lambda Layers are versioned archives of code, data, or libraries that you attach to Lambda functions. Instead of bundling dependencies (like requests, pandas, or boto3) into every function ZIP, you create a single Layer, upload it once, and reference it from multiple functions. This reduces function size, speeds up deployments, and makes dependency management centralized.
What are Lambda Layers and Why Use Them?
A Layer is a ZIP file that unpacks into /opt/python in the Lambda execution environment. Functions that reference the layer automatically have access to its contents. Each layer supports multiple versions, and a function can reference up to 5 layers simultaneously.
Benefits:
- Reduced function size: Function ZIPs stay under 50 MB (uncompressed), reducing upload time and cold-start penalty.
- Code reuse: Share utilities, helper functions, or configuration across multiple functions.
- Centralized updates: Update a library once in the layer; all dependent functions get the new version immediately (for
$LATEST) or on redeploy. - Faster deployments: Uploading a 5 MB function ZIP is faster than a 50 MB ZIP with bundled dependencies.
A typical layer structure:
layer/
python/
requests/ (third-party library)
urllib3/
certifi/
custom_lib/ (your own code)
helpers.py
config.py
When deployed, files in python/ become available at the Python search path in Lambda.
Step 1: Build a Dependency Layer ZIP
First, install dependencies locally into a directory structure matching Lambda's expectations:
# Create directory structure
mkdir -p lambda-layer/python
# Install packages into the layer directory
pip install requests pandas -t lambda-layer/python/
# Verify structure
ls -la lambda-layer/python/ | head -20
# Output: requests/, pandas/, numpy/, etc.
On Windows (PowerShell):
mkdir -Path lambda-layer/python -Force
pip install requests pandas -t lambda-layer/python
dir lambda-layer/python | Select-Object Name
Next, ZIP the directory. The ZIP structure must be:
layer.zip
python/
requests/
pandas/
# ... all installed packages
cd lambda-layer
zip -r layer.zip python/
cd ..
# Verify contents
unzip -l lambda-layer/layer.zip | head -20
PowerShell:
Compress-Archive -Path "lambda-layer/python" -DestinationPath "lambda-layer/layer.zip"
Step 2: Upload the Layer to AWS
Use the AWS CLI to create the layer:
aws lambda publish-layer-version \
--layer-name my-dependencies \
--zip-file fileb://lambda-layer/layer.zip \
--compatible-runtimes python3.12 \
--region us-east-1
Output:
{
"LayerVersionArn": "arn:aws:lambda:us-east-1:123456789012:layer:my-dependencies:1",
"Version": 1,
"CompatibleRuntimes": ["python3.12"],
"CreatedDate": "2026-06-02T10:30:00.000+0000"
}
Save the ARN; you'll reference it when attaching to functions. Each publish-layer-version call increments the version number (1, 2, 3, ...).
Alternatively, upload via the AWS Console:
- Go to Lambda → Layers → Create Layer
- Name:
my-dependencies - Upload the ZIP file
- Compatible runtimes: Check Python 3.12
- Click Create
Step 3: Attach the Layer to a Function
When creating a new function or updating an existing one, add the layer. In the Console:
- Open your Lambda function
- Scroll to Layers section (below the code editor)
- Click Add a layer
- Select Custom layers →
my-dependencies→ Version 1 - Click Add
Via AWS CLI:
aws lambda update-function-configuration \
--function-name my-function \
--layers "arn:aws:lambda:us-east-1:123456789012:layer:my-dependencies:1"
Now your function can import packages from the layer:
import json
import requests # From the layer
import pandas as pd # From the layer
def lambda_handler(event, context):
response = requests.get('https://api.example.com/data')
data = response.json()
df = pd.DataFrame(data)
summary = df.describe().to_dict()
return {
'statusCode': 200,
'body': json.dumps(summary)
}
Building Custom Code Layers
Layers aren't limited to third-party packages; include your own shared utilities:
Create a shared module structure:
mkdir -p lambda-layer/python/my_lib
# Create a shared module
cat > lambda-layer/python/my_lib/__init__.py << 'EOF'
def format_response(status_code, message):
return {
'statusCode': status_code,
'body': message
}
def parse_event(event):
return event.get('body', {})
EOF
# ZIP and upload
cd lambda-layer
zip -r shared-lib.zip python/
cd ..
aws lambda publish-layer-version \
--layer-name shared-utilities \
--zip-file fileb://lambda-layer/shared-lib.zip \
--compatible-runtimes python3.12
In your functions, import the shared code:
from my_lib import format_response, parse_event
def lambda_handler(event, context):
data = parse_event(event)
# Do work
result = f'Processed: {data}'
return format_response(200, result)
Managing Multiple Layer Versions
Each time you publish a new version, AWS increments the version number:
# Version 1: Initial release (requests, pandas)
aws lambda publish-layer-version \
--layer-name my-dependencies \
--zip-file fileb://layer-v1.zip
# Version 2: Add numpy
aws lambda publish-layer-version \
--layer-name my-dependencies \
--zip-file fileb://layer-v2.zip
# Version 3: Update requests to latest
aws lambda publish-layer-version \
--layer-name my-dependencies \
--zip-file fileb://layer-v3.zip
Functions can reference any version. Recommended practice:
- Use specific versions (
:1,:2) in production to avoid surprises - Test new versions with development functions before rolling out
Update a function to a new layer version:
aws lambda update-function-configuration \
--function-name my-function \
--layers "arn:aws:lambda:region:account:layer:my-dependencies:3"
Layer Size and Cold Start Optimization
Layers count toward the 250 MB uncompressed limit (Lambda extracts ZIPs before execution). Keep layers lean:
- Use lightweight packages: Prefer
httpxoverrequestsif possible;ujsonover standardjsonfor speed. - Strip unnecessary files: Remove tests, docs, and
__pycache__:
pip install --no-cache-dir requests -t lambda-layer/python/
# Remove test files
find lambda-layer/python -name "*.pyc" -delete
find lambda-layer/python -name "__pycache__" -exec rm -rf {} + 2>/dev/null
zip -r layer.zip python/ -x "*/tests/*" "*/docs/*"
- Use Lambda Layers for standard library backports: Pin
six,typing-extensionsseparately if multiple functions use them.
Practical Example: Multi-Layer Setup
Combine multiple layers for flexibility:
# Layer 1: Core dependencies
publish-layer-version --layer-name dependencies --zip-file fileb://deps.zip
# Layer 2: Shared utilities
publish-layer-version --layer-name utilities --zip-file fileb://utils.zip
# Function references both
aws lambda update-function-configuration \
--function-name my-function \
--layers \
"arn:aws:lambda:us-east-1:123456789012:layer:dependencies:1" \
"arn:aws:lambda:us-east-1:123456789012:layer:utilities:1"
Layers are resolved in order; if both contain a module with the same name, the first layer in the list takes precedence.
Key Takeaways
- Lambda Layers are ZIP archives of code and dependencies attached to functions, reducing function size and enabling code reuse.
- Install dependencies with
pip install -t layer/python/, ZIP the directory with thepython/folder at the root, and publish withaws lambda publish-layer-version. - Layers must follow the
/opt/pythondirectory structure; Lambda automatically adds this to the Python search path. - Attach layers via the Console or CLI; functions can reference up to 5 layers simultaneously.
- Use specific layer versions (
:1,:2) in production; test new versions before rolling out. - Keep layers under 50 MB uncompressed; strip unnecessary files and use lightweight packages to optimize cold starts.
Frequently Asked Questions
Can I share a layer across AWS accounts?
Yes. Publish the layer in the source account, then use aws lambda add-layer-version-permission to grant the target account permission. Reference the full ARN in the target account. This is ideal for organization-wide utility libraries.
How do I update dependencies without breaking existing functions?
Publish a new layer version. Existing functions continue using the old version until you explicitly update their configuration. This prevents unexpected breaking changes. Always test new versions in a dev environment first.
What's the maximum layer size?
Lambda supports up to 250 MB uncompressed per function. With 5 layers max, the practical limit is roughly 50 MB per layer. Compress aggressively: remove tests, docs, and compiled bytecode. Consider S3 for very large assets.
Can I use compiled packages (like numpy) in layers?
Yes, but compile them on Amazon Linux (matching Lambda's environment). Use AWS CodeBuild, EC2, or Docker with an Amazon Linux container to compile. Pre-compiled wheels from manylinux tags work directly.
How do I debug layer import errors?
Test locally by adding the layer ZIP's python/ directory to sys.path, then import. In Lambda, check CloudWatch Logs for ModuleNotFoundError. Verify the ZIP structure: python/module_name/.
Further Reading
- AWS Lambda Layers Documentation — Official layer specification and configuration
- Python Package Distribution — Best practices for organizing and distributing Python packages
- Creating Lambda Layers with Dependencies — Official guide to ZIP structure for Lambda
- AWS SAM Layers — Infrastructure-as-Code approach to managing layers