AWS Lambda Layers in Practice with Python: Sharing Dependencies Across Functions
Learn how to create an AWS Lambda Layer with Python dependencies and reusable utility functions, then share it across two Lambda functions. This hands-on lab walks you through packaging, publishing, testing, and cleaning up a Layer using the AWS CLI
When we start building multiple AWS Lambda functions, we often notice that some code gets repeated. One function may need an external library, another may use the same dependency, and both may share validators, response formatters, or logging utilities. One option is to include everything in each function's deployment package. This works, but it creates unnecessary duplication.
In this lab, we will take a different approach: put shared components into an AWS Lambda Layer and let each function remain responsible for its own code.
Let's get started!
What Is a Lambda Layer?
An AWS Lambda Layer is a package that contains libraries, dependencies, or other components that can be reused by one or more Lambda functions. Instead of including these files in every function package, we can provide them in a separate layer. When a function uses a Layer, Lambda makes its contents available at runtime, allowing the code to import the modules and libraries included in it.
In practice, this helps you:
- Avoid duplication: share dependencies and common code across multiple functions.
- Simplify maintenance: update shared components without manually bundling them into each function's code.
- Organize the project: keep business logic separate from dependencies and reusable utilities.
- Reduce individual package sizes: remove components that are already available in the Layer.
A Layer has its own versions, each identified by a specific ARN. This means different functions can use different versions of the same Layer. Keep in mind that Layers do not replace dependency management or eliminate the need for compatibility testing. They are a way to organize and share components, especially when multiple functions need the same libraries.
What We Will Build
In this lab, we will create two Python Lambda functions that use the same Layer, which contains an external dependency and a module with helper functions.
The functions will be:
process_order: simulates order processing by validating required fields and checking stock availability.validate_customer: checks a customer's required information and returns details about their record.
The Layer will provide the following components:
requests: a Python library used as an external dependency.shared_utils.py: a module with reusable functions for standardizing responses, validating data, and logging events.
The Problem
Imagine two functions with the following structure:
1
2
3
4
5
6
7
8
9
process-order.zip
├── process_order.py
├── requests/
└── shared_utils.py
validate-customer.zip
├── validate_customer.py
├── requests/
└── shared_utils.pyThe application works, but we are distributing the same dependency and shared code twice. The idea is to follow the DRY principle (Don't Repeat Yourself).
With a Layer, our architecture looks like this:
1
2
3
4
5
6
7
8
9
10
Lambda Layer
┌─────────────────────┐
│ requests │
│ shared_utils.py │
└──────────┬──────────┘
│
┌────────┴────────┐
│ │
▼ ▼
process_order validate_customerThis keeps the function packages smaller, with each function containing only what is specific to its responsibility. Layers also help separate function logic from its dependencies and share components across multiple functions.
Expected Result
The final setup will look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
AWS Lambda
│
Shared Layer
│
┌───────────┴───────────┐
│ │
▼ ▼
process-order validate-customer
│ │
└───────────┬───────────┘
│
shared_utils.py
+
requestsWe will publish the Layer as its own version, and both functions will use that version.
Prerequisites
- AWS CLI configured with a profile that has the permissions needed to create the resources.
- Python 3.
pip.zipandunzip.- An AWS Region for the lab. By default, we will use the infamous and beloved us-east-1.
Step-by-Step Guide
1. Clone the Repository and Configure the Profile
Open the lab's GitHub repository, lab-lambda-layers , then clone it and enter the project directory to get the Python files and
requirements.txt:1
2
git clone https://github.com/ludsilva/lab-lambda-layers.git
cd lab-lambda-layersFor this lab, I will use Linux. We can set the AWS profile and Region for the current terminal session:
1
2
export AWS_PROFILE=teste_lab
export AWS_REGION=us-east-1Note: Replace
teste_lab with the name of the profile you use in AWS.Confirm your identity:
1
aws sts get-caller-identityRun this check before creating resources, especially if you have multiple profiles configured. It confirms that you are using the correct account and credentials—no one wants to discover they ran the lab in the wrong account.
2. Prepare the Layer
For a Python Layer, the ZIP file must have a
python directory at its root. One supported path for libraries is python/lib/python3.x/site-packages. Let's create that structure:1
mkdir -p lambda-layer/python/lib/python3.12/site-packagesNow install the dependencies directly into this structure using the project's
requirements.txt:1
2
pip install -r requirements.txt \
-t lambda-layer/python/lib/python3.12/site-packages/The
requirements.txt file pins the version used in this example:1
requests==2.32.5Pinning the version makes the build more predictable. In real projects, keep this file updated in a controlled way and test changes before publishing a new Layer version.
3. Add Your Own Code to the Layer
A Layer does not have to contain only third-party libraries; it can also provide your own Python modules. In this example, the module is named:
1
shared_utils.pyCopy it to:
1
2
cp shared_utils.py \
lambda-layer/python/lib/python3.12/site-packages/The directory structure should look like this:
1
2
3
4
5
6
7
8
9
10
lambda-layer/
└── python/
└── lib/
└── python3.12/
└── site-packages/
├── requests/
├── urllib3/
├── certifi/
├── ...
└── shared_utils.pyshared_utils.py provides four reusable functions:standard_response(): standardizes the response.validate_request(): checks required fields.log_event(): writes structured JSON logs.requests_version(): returns the version of therequestslibrary to confirm that the dependency is accessible through the Layer.
4. Package the Layer
Change to the directory:
1
cd lambda-layerZip the
python directory so that it remains at the root of the ZIP archive:1
zip -r ../shared-python-components.zip pythonReturn to the parent directory and inspect the contents:
1
2
cd ..
unzip -l shared-python-components.zipThe ZIP must contain
python/ at the root, not an outer directory such as lambda-layer/python/.5. Publish the Layer
Get the account ID:
1
2
3
ACCOUNT_ID=$(aws sts get-caller-identity \
--query Account \
--output text)Publish the Layer:
1
2
3
4
5
6
7
aws lambda publish-layer-version \
--layer-name shared-python-components \
--region "$AWS_REGION" \
--description "Shared Python dependencies and utilities" \
--zip-file fileb://shared-python-components.zip \
--compatible-runtimes python3.12 \
--compatible-architectures x86_64Save the ARN returned by the command. It identifies a specific version of the Layer. The ARN will have a format similar to this:
1
arn:aws:lambda:${AWS_REGION}:${ACCOUNT_ID}:layer:shared-python-components:1Store the ARN in a variable, replacing the example version number with the one returned by the publication command:
1
LAYER_ARN="arn:aws:lambda:${AWS_REGION}:${ACCOUNT_ID}:layer:shared-python-components:1"Note:
1 is only an example. Check the actual ARN returned when you publish the Layer.6. Create the IAM Role
Lambda functions need an IAM role that allows the service to assume the role and write logs to CloudWatch. If you already have a suitable role, you can reuse it.
If you need to create a role for this lab, create the trust policy file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
cat > trust-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOFCreate the role:
1
2
3
aws iam create-role \
--role-name lambda-layers-lab-role \
--assume-role-policy-document file://trust-policy.jsonAttach the basic execution policy:
1
2
3
aws iam attach-role-policy \
--role-name lambda-layers-lab-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRoleGet the ARN:
1
2
3
4
ROLE_ARN=$(aws iam get-role \
--role-name lambda-layers-lab-role \
--query 'Role.Arn' \
--output text)7. Package and Create the process-order Function
The function package should contain only the handler code. Do not include
requests or shared_utils.py, because these components will be provided by the Layer.1
zip process-order.zip process_order.pyCreate the function:
1
2
3
4
5
6
7
aws lambda create-function \
--function-name process-order \
--runtime python3.12 \
--handler process_order.lambda_handler \
--zip-file fileb://process-order.zip \
--role "$ROLE_ARN" \
--layers "$LAYER_ARN"The Layer ARN must point to a specific version.
8. Package and Create the validate-customer Function
Repeat the process for the second function:
1
zip validate-customer.zip validate_customer.pyCreate the function:
1
2
3
4
5
6
7
aws lambda create-function \
--function-name validate-customer \
--runtime python3.12 \
--handler validate_customer.lambda_handler \
--zip-file fileb://validate-customer.zip \
--role "$ROLE_ARN" \
--layers "$LAYER_ARN"Both functions are now associated with the same Layer version.
9. Test the Order Function
Create an order event:
1
2
3
4
5
6
7
cat > order-event.json <<'EOF'
{
"product_id": "SKU-8472",
"quantity": 4,
"customer_id": "CUS-208"
}
EOFInvoke the function:
1
2
3
4
5
aws lambda invoke \
--function-name process-order \
--payload fileb://order-event.json \
--cli-binary-format raw-in-base64-out \
order-response.jsonView the response:
1
cat order-response.jsonThe response should contain the order data, the processing status, and the version of
requests provided by the Layer.10. Test Required-Field Validation
Now send an incomplete event:
1
2
3
4
5
cat > invalid-order-event.json <<'EOF'
{
"product_id": "SKU-8472"
}
EOFInvoke the function:
1
2
3
4
5
aws lambda invoke \
--function-name process-order \
--payload fileb://invalid-order-event.json \
--cli-binary-format raw-in-base64-out \
invalid-order-response.jsonCheck the result:
1
cat invalid-order-response.jsonThe function should return a response with
statusCode set to 400, indicating the missing fields.11. Test the Customer Function
Create a valid event:
1
2
3
4
5
6
cat > customer-event.json <<'EOF'
{
"email": "ana@example.com",
"name": "Ana"
}
EOFInvoke the function:
1
2
3
4
5
aws lambda invoke \
--function-name validate-customer \
--payload fileb://customer-event.json \
--cli-binary-format raw-in-base64-out \
customer-response.jsonView the response:
1
cat customer-response.jsonThe response should include the customer data and the version of the
requests library provided by the Layer.12. Verify the Layer on the Function
To confirm the setup, check the Layers configured on the order function:
1
2
3
4
aws lambda get-function-configuration \
--function-name process-order \
--query 'Layers[*].Arn' \
--output textRepeat this for the customer function:
1
2
3
4
aws lambda get-function-configuration \
--function-name validate-customer \
--query 'Layers[*].Arn' \
--output textThe commands should return the ARN of the Layer version associated with each function.
13. Understand How the Layer Becomes Available
At runtime, Lambda makes the Layer contents available in the
/opt directory. For the Python runtime, the import paths are configured so that modules in the Layer can be found. That is why the handler can import them directly:1
2
3
from shared_utils import standard_response, validate_request
import requestsThese components do not need to be duplicated in the function ZIP because the Layer provides them.
14. List Layer Versions
List the published versions:
1
2
3
aws lambda list-layer-versions \
--layer-name shared-python-components \
--region "$AWS_REGION"Each publication creates a new immutable version. If a function uses version
1, it will not automatically switch to version 2 when a new version is published. The update must be made explicitly.15. Clean Up the Lab
To finish the lab, delete the functions:
1
2
3
4
5
aws lambda delete-function \
--function-name process-order
aws lambda delete-function \
--function-name validate-customerDelete the published Layer version, and change the number if the version you created is different from
1:1
2
3
4
aws lambda delete-layer-version \
--layer-name shared-python-components \
--version-number 1 \
--region "$AWS_REGION"If you created the IAM role exclusively for this lab, detach the policy and delete the role:
1
2
3
4
5
6
aws iam detach-role-policy \
--role-name lambda-layers-lab-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam delete-role \
--role-name lambda-layers-lab-roleIf you want, remove the files generated locally:
1
2
3
4
5
6
rm -rf lambda-layer
rm -f shared-python-components.zip
rm -f process-order.zip validate-customer.zip
rm -f trust-policy.json
rm -f order-event.json invalid-order-event.json customer-event.json
rm -f order-response.json invalid-order-response.json customer-response.jsonDo not delete roles that are used by other labs or applications.
16. Limits and Quotas
A Lambda function can have up to five Layers attached. The combined unzipped size of the function package and its Layers must stay within the 250 MB limit.
This does not mean we need to create five Layers for every function. The separation should make sense for the architecture.
For example, a shared utilities Layer can serve multiple functions. Creating a separate Layer for every small file, on the other hand, can increase operational complexity.
17. When Does It Make Sense to Use Layers?
Some common scenarios:
- Shared dependencies: multiple functions use the same library or set of libraries.
- Reusable internal code: logging, validation, or response-handling modules are used by multiple functions.
- Separation of responsibilities: the function package contains application logic, while the Layer holds shared components.
- Controlled updates: a new version can be published and explicitly adopted by functions.
18. When Might a Layer Be Unnecessary?
If only one function uses a particular library, adding a Layer may not provide enough benefit to justify the extra complexity. Including the dependency directly in the function package may be simpler.
It is also important to consider dependency compatibility. Python libraries with native components must be compatible with the Linux environment and architecture used by Lambda. When needed, build the package in a compatible environment, for example, using Docker.
19. Reproducibility Considerations
Installing dependencies without pinning versions can produce different results across builds run at different times.
In this lab,
requirements.txt specifies an explicit version:1
requests==2.32.5In real projects, keep dependencies updated in a controlled way and test compatibility before publishing a new Layer version. Pinning versions makes builds more predictable, but it does not replace testing or security reviews.
Conclusion
And that's the end of our lab!
Throughout this walkthrough, we created an AWS Lambda Layer containing the
requests library and our own utility module, published a version of the Layer, and shared it between two Python Lambda functions. We also tested the functions to verify that the components provided by the Layer were available at runtime.This showed us how to centralize shared dependencies without including them in every function package. In addition to reducing duplication, this approach helps organize and maintain code when different functions need the same components.
Now it's your turn: try adapting this lab to the dependencies and utilities you already use in your projects. It's a great way to reinforce what you've learned and see how Lambda Layers can simplify the development of your functions.
See you next time!
References
- AWS Lambda - Managing Lambda dependencies with layers
- AWS Lambda - Working with layers for Python Lambda functions
- AWS Lambda - Packaging your layer content
- AWS Lambda - Working with .zip file archives for Python Lambda functions
- AWS Lambda - Adding layers to functions
- AWS Lambda - Quotas
- AWS CLI -
publish-layer-version - Article - Introduction to AWS Lambda: A Starter Guide
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article