Serverless Go Plugin
This Serverless Framework plugin, currently at version 2.4.1, automates the compilation of Go functions for AWS Lambda deployments. It integrates directly with Serverless Framework commands like `deploy`, `deploy function`, and `invoke local`, compiling Go source code on the fly before packaging. Key differentiators include concurrent compilation across all CPU cores for efficiency, support for various Go runtimes including `go1.x` and `provided.al2` (with bootstrap), and features for monorepo-like project structures. The plugin manages handler path transformations and package exclusions/inclusions, simplifying the deployment workflow for Go-based serverless applications. Releases generally include minor feature enhancements, dependency updates, and bug fixes, maintaining an active development cadence. It specifically requires Serverless Framework version 1.52 or above to function.
Common errors
-
Error: Cannot find module 'serverless-go-plugin'
cause Attempting to `require` or `import` the plugin in a JavaScript/TypeScript file, instead of configuring it in `serverless.yaml`.fixRemove any JavaScript/TypeScript `require` or `import` statements for `serverless-go-plugin`. Ensure the plugin is listed correctly under the `plugins` section in your `serverless.yaml` file: `plugins: - serverless-go-plugin`. -
go build <module path> failed with exit status 1
cause A generic Go compilation error indicating an issue with your Go source code, build environment, or `cmd` configuration.fixReview your Go source code for syntax errors, missing dependencies (check `go.mod`), or type mismatches. Verify your `custom.go.cmd` setting for any incorrect build flags or commands. Ensure Go is correctly installed and accessible in your environment. -
Lambda Validation Error: Your handler 'bootstrap' is not found in the deployment package.
cause This error often occurs when deploying to `provided.al2` or similar custom runtimes without correctly configuring the plugin to build a `bootstrap` executable or without including it in the package.fixEnsure `runtime: provided.al2` is set for the function and that `custom.go.buildProvidedRuntimeAsBootstrap: true` is enabled in your plugin configuration. Also, append `GOARCH=arm64` (or target architecture) to your `custom.go.cmd`.
Warnings
- gotcha When deploying Go Lambda functions on ARM64 architecture using `provided.al2` runtime, the very first deployment may result in a small downtime (a few seconds) as the new runtime initializes. Consider using a deployment strategy like canary deployments for safer rollouts.
- gotcha The `handler` property for Go functions must specify the path to the Go source file (e.g., `functions/myFunc/main.go`) or the package path (e.g., `functions/myFunc`). Unlike Node.js or Python, simply using the function name is incorrect and will lead to handler not found errors.
- gotcha If you configure the `custom.go.baseDir` property in `serverless.yaml`, all `handler` paths for your Go functions must be specified relative to this `baseDir`, not relative to the `serverless.yaml` file itself. Misconfiguration can lead to compilation failures or handlers not being found.
Install
-
npm install serverless-go-plugin -
yarn add serverless-go-plugin -
pnpm add serverless-go-plugin
Imports
- Plugin Activation
import 'serverless-go-plugin'; // Incorrect for Serverless plugins // OR const plugin = require('serverless-go-plugin');plugins: - serverless-go-plugin
- Go Function Handler Definition
functions: myGoFunc: runtime: go1.x handler: myGoFunc // Assumes default JS/Node.js handler naming conventionsfunctions: myGoFunc: runtime: go1.x handler: functions/myGoFunc/main.go - Custom Plugin Configuration
custom: go: { binDir: ".bin" } // Valid YAML but less readable; also, direct programmatic access via JS is not applicablecustom: go: binDir: .bin cgo: 1 monorepo: true
Quickstart
{
"name": "my-go-serverless-app",
"version": "1.0.0",
"description": "A serverless Go application with serverless-go-plugin",
"main": "handler.js",
"scripts": {
"deploy": "serverless deploy"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"serverless": "^3.0.0",
"serverless-go-plugin": "^2.0.0",
"@aws-cdk/aws-lambda": "^1.0.0"
}
}
// serverless.yaml
service: my-go-app
frameworkVersion: '3'
provider:
name: aws
runtime: go1.x # Default runtime, can be overridden per function
region: us-east-1
architecture: x86_64 # Default architecture
plugins:
- serverless-go-plugin
custom:
go:
binDir: .bin # Target folder for compiled binaries
cmd: 'GOOS=linux go build -ldflags="-s -w"' # Default compile command
supportedRuntimes: ["go1.x", "provided.al2"]
functions:
hello:
handler: functions/hello/main.go # Path to the Go source file
events:
- httpApi:
path: /hello
method: get
greetArm:
runtime: provided.al2 # Example for a bootstrapped runtime
handler: functions/greet # Package path (assuming main.go inside)
architecture: arm64
environment:
MESSAGE: "Hello from ARM64 Go Lambda!"
events:
- httpApi:
path: /greet/{name}
method: get
// functions/hello/main.go
package main
import (
"context"
"fmt"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
type Response events.APIGatewayProxyResponse
func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (Response, error) {
body := fmt.Sprintf("Hello from Go Lambda on x86_64! Path: %s", request.Path)
resp := Response{
StatusCode: 200,
IsBase64Encoded: false,
Body: body,
Headers: map[string]string{
"Content-Type": "application/json"
},
}
return resp, nil
}
func main() {
lambda.Start(Handler)
}
// functions/greet/main.go
package main
import (
"context"
"fmt"
"net/http"
"os"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
func GreetHandler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
name := request.PathParameters["name"]
if name == "" {
name = "stranger"
}
messageEnv := os.Getenv("MESSAGE")
message := fmt.Sprintf("%s Greetings, %s!", messageEnv, name)
return events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Headers: map[string]string{"Content-Type": "text/plain"},
Body: message,
},
nil
}
func main() {
lambda.Start(GreetHandler)
}