When I first started building serverless functions, I was drawn to Go for one primary reason: speed. However, I quickly realized that deploying Go apps to AWS Lambda isn’t as simple as pushing code to a repository. Because Go is a compiled language, you have to ensure your binary is built for the specific Linux environment AWS uses, or you’ll be greeted by the dreaded /var/task/bootstrap: permission denied or exec format error.
In this tutorial, I’ll walk you through my proven workflow for getting a Go application from your local machine into production on AWS Lambda, focusing on the modern provided.al2023 runtime. If you’re wondering why use Go for cloud native development, the answer is usually the combination of low memory overhead and near-instant startup times—perfect for serverless.
Prerequisites
- Go 1.21 or later installed locally.
- An AWS Account and the AWS CLI configured with appropriate IAM permissions.
- A basic understanding of the Go module system.
- (Optional) Docker installed, which helps in optimizing Go Docker image size if you choose the container image deployment route.
Step 1: Writing the Lambda Handler
AWS Lambda requires a specific handler function. Unlike a standard Go main() package that might start an HTTP server, a Lambda function uses the github.com/aws/aws-lambda-go/lambda package to listen for events.
package main
import (
"context"
"fmt"
"github.com/aws/aws-lambda-go/lambda"
)
type MyEvent struct {
Name string `json:"name"`
}
type MyResponse struct {
Message string `json:"message"`
}
func HandleRequest(ctx context.Context, name MyEvent) (MyResponse, error) {
return MyResponse{Message: fmt.Sprintf("Hello %s!", name.Name)}, nil
}
func main() {
lambda.Start(HandleRequest)
}
Step 2: Compiling for the Lambda Environment
This is where most developers fail. You cannot simply run go build on a Mac or Windows machine and upload the result. You must cross-compile the binary for Linux and name it bootstrap to work with the provided.al2023 runtime.
Run the following commands in your terminal:
# Set target OS and Architecture
export GOOS=linux
export GOARCH=amd64
# Build the binary as 'bootstrap'
go build -o bootstrap main.go
# Zip the binary for upload
zip function.zip bootstrap
As shown in the image below, the bootstrap filename is mandatory for the custom runtime. If you name it main, AWS won’t know how to execute it.
Step 3: Deploying via AWS CLI
While the AWS Console is fine for beginners, I always recommend the CLI for repeatability. Use the following command to create your function:
aws lambda create-function \
--function-name my-go-lambda \
--runtime provided.al2023 \
--handler bootstrap \
--role arn:aws:iam::123456789012:role/lambda-ex \
--zip-file fileb://function.zip
Note that the --runtime provided.al2023 flag tells AWS to use the latest Amazon Linux 2023 image, which is significantly faster and more secure than the older AL2.
Pro Tips for Go Lambdas
- Global State for Connection Pooling: Initialize your database connections or AWS SDK clients outside the
HandleRequestfunction. This allows the connection to be reused across “warm” invocations, drastically reducing latency. - Reduce Binary Size: Use the
-ldflags="-s -w"flag during build to strip debug information and reduce the ZIP size. - Architecture Choice: If you’re looking for better price-performance, switch
GOARCHtoarm64and select the Graviton2 processor in the AWS Console.
Troubleshooting Common Errors
If your function fails immediately, check these three things first:
- The filename: Is the binary named exactly
bootstrap? - The Architecture: Did you compile for
amd64but selectarm64(Graviton) in the AWS settings? - Permissions: Does the binary have execution permissions? If you are building on a non-Linux machine and zipping, ensure the zip process preserves the executable bit.
What’s Next?
Now that you’ve mastered deploying Go apps to AWS Lambda, you might want to look into Lambda Layers for sharing common libraries or AWS SAM (Serverless Application Model) to manage your infrastructure as code. For those building larger systems, exploring how to optimize Go Docker image size is a great next step if you decide to move toward Fargate or Kubernetes.