Working with layers for Ruby Lambda functions
Use Lambda layers to package code and dependencies that you want to reuse across multiple functions. Layers usually contain library dependencies, a custom runtime, or configuration files. Creating a layer involves three general steps:
-
Package your layer content. This means creating a .zip file archive that contains the dependencies you want to use in your functions.
-
Create the layer in Lambda.
-
Add the layer to your functions.
Topics
Package your layer content
To create a layer, bundle your packages into a .zip file archive that meets the following requirements:
-
Create the layer using the same Ruby version that you plan to use for the Lambda function. For example, if you create your layer for Ruby 3.4, use the Ruby 3.4 runtime for your function.
-
Your layer's .zip file must use one of these directory structures:
-
ruby/gems/
(wherex.x.x
x.x.x
is your Ruby version, for example3.4.0
) -
ruby/lib
For more information, see Layer paths for each Lambda runtime.
-
-
The packages in your layer must be compatible with Linux. Lambda functions run on Amazon Linux.
You can create layers that contain either third-party Ruby gems or your own Ruby modules and classes. Many popular Ruby gems contain native extensions (C code) that must be compiled for the Lambda Linux environment.
Pure Ruby gems contain only Ruby code and don't require compilation. These gems are simpler to package and work across different platforms.
To create a layer using pure Ruby gems
-
Create a
Gemfile
to specify the pure Ruby gems you want to include in your layer:Example Gemfile
source 'https://rubygems.org' gem 'tzinfo'
-
Install the gems to
vendor/bundle
directory using Bundler:bundle config set --local path vendor/bundle bundle install
-
Copy the installed gems to the directory structure that Lambda requires
ruby/gems/3.4.0
):mkdir -p ruby/gems/3.4.0 cp -r vendor/bundle/ruby/3.4.0*/* ruby/gems/3.4.0/
-
Zip the layer content:
The directory structure of your .zip file should look like this:
ruby/ └── gems/ └── 3.4.0/ ├── gems/ │ ├── concurrent-ruby-1.3.5/ │ └──
tzinfo-2.0.6/
├── specifications/ ├── cache/ ├── build_info/ └── (other bundler directories)Note
You must require each gem individually in your function code. You can't use
bundler/setup
orBundler.require
. For more information, see Using gems from layers in a function.
Many popular Ruby gems contain native extensions (C code) that must be compiled for the target platform. Popular gems with native extensions include nokogiri
To create a layer using gems with native extensions
-
Create a
Gemfile
.Example Gemfile
source 'https://rubygems.org' gem 'nokogiri' gem 'httparty'
-
Use Docker to build the gems in a Linux environment that is compatible with Lambda. Specify an Amazon base image in your Dockerfile:
Example Dockerfile for Ruby 3.4
FROM
public.ecr.aws/lambda/ruby:3.4
# Copy Gemfile COPY Gemfile ./ # Install system dependencies for native extensions RUN dnf update -y && \ dnf install -y gcc gcc-c++ make # Configure bundler and install gems RUN bundle config set --local path vendor/bundle && \ bundle install # Create the layer structure RUN mkdir -p ruby/gems/3.4.0 && \ cp -r vendor/bundle/ruby/3.4.0
*/* ruby/gems/3.4.0
/ # Create the layer zip file RUN zip -r layer.zip ruby/ -
Build the image and extract the layer:
docker build -t ruby-layer-builder . docker run --rm -v $(pwd):/output --entrypoint cp ruby-layer-builder layer.zip /output/
This builds the gems in the correct Linux environment and copies the
layer.zip
file to your local directory. The directory structure of your .zip file should look like this:ruby/ └── gems/ └── 3.4.0/ ├── gems/ │ ├── bigdecimal-3.2.2/ │ ├── csv-3.3.5/ │ ├──
httparty-0.23.1/
│ ├── mini_mime-1.1.5/ │ ├── multi_xml-0.7.2/ │ ├──nokogiri-1.18.8-x86_64-linux-gnu/
│ └── racc-1.8.1/ ├── build_info/ ├── cache/ ├── specifications/ └── (other bundler directories)Note
You must require each gem individually in your function code. You can't use
bundler/setup
orBundler.require
. For more information, see Using gems from layers in a function.
To create a layer using your own code
-
Create the required directory structure for your layer:
mkdir -p ruby/lib
-
Create your Ruby modules in the
ruby/lib
directory. The following example module validates orders by confirming that they contain the required information.Example ruby/lib/order_validator.rb
require 'json' module OrderValidator class ValidationError < StandardError; end def self.validate_order(order_data) # Validates an order and returns formatted data required_fields = %w[product_id quantity] # Check required fields missing_fields = required_fields.reject { |field| order_data.key?(field) } unless missing_fields.empty? raise ValidationError, "Missing required fields: #{missing_fields.join(', ')}" end # Validate quantity quantity = order_data['quantity'] unless quantity.is_a?(Integer) && quantity > 0 raise ValidationError, 'Quantity must be a positive integer' end # Format and return the validated data { 'product_id' => order_data['product_id'].to_s, 'quantity' => quantity, 'shipping_priority' => order_data.fetch('priority', 'standard') } end def self.format_response(status_code, body) # Formats the API response { statusCode: status_code, body: JSON.generate(body) } end end
-
Zip the layer content:
The directory structure of your .zip file should look like this:
ruby/ └── lib/ └── order_validator.rb
-
In your function, require and use the modules. You must require each gem individually in your function code. You can't use
bundler/setup
orBundler.require
. For more information, see Using gems from layers in a function. Example:require 'json' require 'order_validator' def lambda_handler(event:, context:) begin # Parse the order data from the event body order_data = JSON.parse(event['body'] || '{}') # Validate and format the order validated_order = OrderValidator.validate_order(order_data) OrderValidator.format_response(200, { message: 'Order validated successfully', order: validated_order }) rescue OrderValidator::ValidationError => e OrderValidator.format_response(400, { error: e.message }) rescue => e OrderValidator.format_response(500, { error: 'Internal server error' }) end end
You can use the following test event to invoke the function:
{ "body": "{\"product_id\": \"ABC123\", \"quantity\": 2, \"priority\": \"express\"}" }
Expected response:
{ "statusCode": 200, "body": "{\"message\":\"Order validated successfully\",\"order\":{\"product_id\":\"ABC123\",\"quantity\":2,\"shipping_priority\":\"express\"}}" }
Create the layer in Lambda
You can publish your layer using either the Amazon CLI or the Lambda console.
Using gems from layers in a function
In your function code, you must explicitly require each gem that you want to use. Bundler commands such as bundler/setup
and Bundler.require
are not supported. Here's how to properly use gems from a layer in a Lambda function:
# Correct: Use explicit requires for each gem require 'nokogiri' require 'httparty' def lambda_handler(event:, context:) # Use the gems directly doc = Nokogiri::HTML(event['html']) response = HTTParty.get(event['url']) # ... rest of your function end # Incorrect: These Bundler commands will not work # require 'bundler/setup' # Bundler.require
Add the layer to your function
Sample app
For more examples of how to use Lambda layers, see the layer-ruby