We're about to build your first real AWS Lambda function in Ruby. But before we touch AWS, we need to get your development environment set up with Docker, the key to making Ruby work seamlessly with Lambda.
If you've never touched Docker before, don't worry. You'll get everything you need here, just enough to be dangerous.
AWS gives you two ways to package and deploy Lambda functions: you can upload a zip file with your code, or you can use a Docker container image.
In this guide, we'll focus on the container approach. It gives you complete control over your Ruby environment, lets you use any version of Ruby, and makes it easy to include complex dependencies. You just build a Docker image with your Ruby code and push it to AWS.
Here's what you get with Docker:
With Docker, you stop fighting AWS and start focusing on your code.
💡 Follow Along: We're about to build a Lambda function template that you'll use throughout this entire guide. I recommend creating this project structure on your local machine and following along with each step, as we'll be extending and reusing this template in every subsequent chapter.
Here's the basic, repeatable structure we'll use for every function in this book:
my_lambda_function/
├── services/
│ └── service.rb # Core business logic
├── examples/
│ └── example.rb # Local test runner
├── function.rb # Entry point for Lambda
├── boot.rb # Loads app dependencies
├── Dockerfile # Defines container environment
├── docker-compose.yml # Local dev/test support
├── Makefile # Optional helper commands
├── Gemfile
├── Gemfile.lock
Let's break that down.
function.rb
– Your Lambda EntrypointLambda containers require a file called function.handler
.
This is your entrypoint. It should define a handler(event:, context:)
method, AWS will call this when your function is triggered.
💡 How this works: Under the hood, AWS maps function.handler
to the handler
method in your function.rb
file. The event
parameter contains the trigger data (like an S3 upload event or HTTP request), and context
provides runtime information about the Lambda execution environment. For more details on Ruby handler best practices, check out the AWS Lambda Ruby documentation.
# function.rb
require_relative './boot'
require_relative './services/service'
def handler(event:, context:)
result = Service.call(event)
puts "Function finished: #{result.inspect}"
result
end
This file is dead simple. It wires together your code and passes the AWS event to your logic.
boot.rb
– Setting Up the EnvironmentThis file loads your environment. Think of it like a mini version of config/application.rb
in Rails.
# boot.rb
require 'bundler/setup'
require 'json'
require 'logger'
Bundler.require
$logger = Logger.new($stdout)
You can add anything here that you want preloaded, libraries, loggers, environment setup, etc.
services/service.rb
– Your Business LogicThis is where the real work happens. Keep your logic here. This keeps your Lambda clean, testable, and easy to reuse.
# services/service.rb
class Service
def self.call(event)
$logger.info("Processing event: #{event.inspect}")
{ message: "Hello from Lambda!", input: event }
end
end
examples/example.rb
– Local Test RunnerThis file lets you test your Lambda logic outside of AWS.
# examples/example.rb
require_relative '../boot'
require_relative '../services/service'
event = { "name" => "Zayne", "action" => "test" }
puts Service.call(event)
You can run this locally with ruby examples/example.rb
. It's a great sanity check before deploying.
Dockerfile
Here's a production-ready Dockerfile using Ruby 3.3:
# Use the official AWS Lambda Ruby 3.3 base image
FROM public.ecr.aws/lambda/ruby:3.3
# Set environment
ARG ENVIRONMENT=production
ENV BUNDLE_PATH="/var/task/vendor/bundle"
ENV BUNDLE_WITHOUT="development:test"
# Set working directory to Lambda task root
WORKDIR $LAMBDA_TASK_ROOT
# Install build dependencies
RUN dnf -y update && \
dnf -y install gcc make ca-certificates && \
dnf clean all
# Install gems
COPY Gemfile Gemfile.lock ./
RUN bundle install --path vendor/bundle --without development test
# Copy the rest of the code
COPY . .
# Lambda entrypoint
CMD [ "function.handler" ]
💡 Lambda will call function.handler
, which is why function.rb
defines a handler
method.
# Gemfile
source "https://rubygems.org"
# Example gems
gem 'aws-sdk-s3'
gem "json"
group :development do
gem 'pry'
end
group :test do
gem 'rspec'
end