|
| 1 | +--- |
| 2 | +id: aws-lambda |
| 3 | +title: Deploy a Serverless Worker on AWS Lambda |
| 4 | +sidebar_label: AWS Lambda |
| 5 | +description: Deploy a Temporal Serverless Worker on AWS Lambda. |
| 6 | +slug: /production-deployment/worker-deployments/serverless-workers/aws-lambda |
| 7 | +toc_max_heading_level: 4 |
| 8 | +keywords: |
| 9 | + - serverless |
| 10 | + - lambda |
| 11 | + - aws |
| 12 | + - worker |
| 13 | + - deployment |
| 14 | +tags: |
| 15 | + - Workers |
| 16 | + - Deploy |
| 17 | + - Serverless |
| 18 | + - AWS Lambda |
| 19 | +--- |
| 20 | + |
| 21 | +import SdkTabs from '@site/src/components/elements/SdkTabs'; |
| 22 | + |
| 23 | +This guide walks through deploying a Temporal Worker on AWS Lambda. |
| 24 | + |
| 25 | +## Prerequisites {#prerequisites} |
| 26 | + |
| 27 | +- A Temporal Cloud account or a self-hosted Temporal Service vx.xx.x or later. |
| 28 | + - Your Temporal Service frontend must be reachable from the Lambda execution environment. For Temporal Cloud, no additional configuration is needed. For self-hosted deployments on a private network, configure the Lambda function with [VPC access](https://docs.aws.amazon.com/lambda/latest/dg/configuration-vpc.html) to reach the Temporal frontend. |
| 29 | +- An AWS account with permissions to create and invoke Lambda functions and create IAM roles. |
| 30 | +- The AWS-specific steps in this guide require the [`aws` CLI](https://aws.amazon.com/cli/) installed and configured with your AWS credentials. You may use other tools to perform the steps, such as the AWS Console or the AWS SDKs. |
| 31 | + |
| 32 | + |
| 33 | +<SdkTabs hideUnsupportedLanguages> |
| 34 | +<SdkTabs.Go> |
| 35 | + |
| 36 | +- The [Go SDK](/develop/go) (`go.temporal.io/sdk`) |
| 37 | + |
| 38 | +</SdkTabs.Go> |
| 39 | +</SdkTabs> |
| 40 | + |
| 41 | +## 1. Write the Worker code {#write-the-worker-code} |
| 42 | + |
| 43 | +Write a Worker that runs inside a Lambda function. |
| 44 | +The Worker handles the per-invocation lifecycle: connecting to Temporal, polling for tasks, and gracefully shutting down before the Lambda deadline. |
| 45 | + |
| 46 | +<SdkTabs hideUnsupportedLanguages> |
| 47 | +<SdkTabs.Go> |
| 48 | + |
| 49 | +Use the Go SDK's `lambdaworker` package. |
| 50 | + |
| 51 | +```go |
| 52 | +package main |
| 53 | + |
| 54 | +import ( |
| 55 | + lambdaworker "go.temporal.io/sdk/contrib/aws/lambdaworker" |
| 56 | + "go.temporal.io/sdk/worker" |
| 57 | + "go.temporal.io/sdk/workflow" |
| 58 | +) |
| 59 | + |
| 60 | +func main() { |
| 61 | + lambdaworker.RunWorker(worker.WorkerDeploymentVersion{ |
| 62 | + DeploymentName: "my-app", |
| 63 | + BuildID: "build-1", |
| 64 | + }, func(opts *lambdaworker.Options) error { |
| 65 | + opts.TaskQueue = "my-task-queue" |
| 66 | + |
| 67 | + opts.RegisterWorkflowWithOptions(MyWorkflow, workflow.RegisterOptions{ |
| 68 | + VersioningBehavior: workflow.VersioningBehaviorAutoUpgrade, |
| 69 | + }) |
| 70 | + opts.RegisterActivity(MyActivity) |
| 71 | + |
| 72 | + return nil |
| 73 | + }) |
| 74 | +} |
| 75 | +``` |
| 76 | + |
| 77 | +Each Workflow must declare a [versioning behavior](/worker-versioning#versioning-behaviors) at registration time, either `AutoUpgrade` or `Pinned`. |
| 78 | + |
| 79 | +For details on configuration options, Lambda-tuned defaults, and the invocation lifecycle, see [Serverless Workers - Go SDK](/develop/go/workers/serverless-workers/aws-lambda). |
| 80 | + |
| 81 | +</SdkTabs.Go> |
| 82 | +</SdkTabs> |
| 83 | + |
| 84 | +## 2. Deploy the Lambda function {#deploy-the-lambda-function} |
| 85 | + |
| 86 | +Your Lambda function needs an [execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html) that grants it permission to run. |
| 87 | +If you don't already have one, create a role with `lambda.amazonaws.com` as the trusted principal before proceeding. |
| 88 | +This role is separate from the IAM role that Temporal uses to invoke the function. |
| 89 | + |
| 90 | +### i. Build and package {#build-and-package} |
| 91 | + |
| 92 | +<SdkTabs hideUnsupportedLanguages> |
| 93 | +<SdkTabs.Go> |
| 94 | + |
| 95 | +Cross-compile for Lambda's Linux runtime: |
| 96 | + |
| 97 | +```bash |
| 98 | +GOOS=linux GOARCH=amd64 go build -tags lambda.norpc -o bootstrap ./worker |
| 99 | +``` |
| 100 | + |
| 101 | +Package the binary into a zip file: |
| 102 | + |
| 103 | +```bash |
| 104 | +zip function.zip bootstrap |
| 105 | +``` |
| 106 | + |
| 107 | +</SdkTabs.Go> |
| 108 | +</SdkTabs> |
| 109 | + |
| 110 | +### ii. Deploy the Lambda function {#deploy-lambda-function} |
| 111 | + |
| 112 | +Configure the Temporal connection using Lambda environment variables. |
| 113 | +The `lambdaworker` package reads these automatically at startup. |
| 114 | + |
| 115 | +| Variable | Description | Required | |
| 116 | +|---|---|---| |
| 117 | +| `TEMPORAL_ADDRESS` | Temporal frontend address (e.g., `<namespace>.<account>.tmprl.cloud:7233`). | Yes | |
| 118 | +| `TEMPORAL_NAMESPACE` | Temporal Namespace. | Yes | |
| 119 | +| `TEMPORAL_TASK_QUEUE` | Task Queue name. Overrides the value set in code. | No | |
| 120 | +| `TEMPORAL_TLS_CERT` | TLS client certificate content for mTLS authentication. | For mTLS | |
| 121 | +| `TEMPORAL_TLS_KEY` | TLS client key content for mTLS authentication. | For mTLS | |
| 122 | +| `TEMPORAL_API_KEY` | API key for API key authentication. | For API key auth | |
| 123 | + |
| 124 | +For the full list of supported environment variables, see [Client environment configuration](/references/client-environment-configuration). |
| 125 | + |
| 126 | +Sensitive values like TLS keys and API keys should be encrypted at rest. See [AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars-encryption.html) for options. |
| 127 | + |
| 128 | +```bash |
| 129 | +aws lambda create-function \ |
| 130 | + --function-name my-temporal-worker \ |
| 131 | + --runtime provided.al2023 \ |
| 132 | + --handler bootstrap \ |
| 133 | + --architectures x86_64 \ |
| 134 | + --role arn:aws:iam::<YOUR_ACCOUNT_ID>:role/my-temporal-worker-execution \ |
| 135 | + --zip-file fileb://function.zip \ |
| 136 | + --timeout 60 \ |
| 137 | + --memory-size 256 \ |
| 138 | + --environment "Variables={TEMPORAL_ADDRESS=<your-temporal-address>:7233,TEMPORAL_NAMESPACE=<your-namespace>}" |
| 139 | +``` |
| 140 | + |
| 141 | +To update an existing function with new code: |
| 142 | + |
| 143 | +```bash |
| 144 | +aws lambda update-function-code \ |
| 145 | + --function-name my-temporal-worker \ |
| 146 | + --zip-file fileb://function.zip |
| 147 | +``` |
| 148 | + |
| 149 | +## 3. Configure IAM for Temporal invocation {#configure-iam} |
| 150 | + |
| 151 | +Temporal needs permission to invoke your Lambda function. |
| 152 | +The Temporal server assumes an IAM role in your AWS account to call `lambda:InvokeFunction`. |
| 153 | +The trust policy on the role includes an External ID condition to prevent [confused deputy](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html) attacks. |
| 154 | + |
| 155 | +Deploy the following CloudFormation template to create the invocation role and its permissions. |
| 156 | + |
| 157 | +| Parameter | Description | |
| 158 | +|---|---| |
| 159 | +| `TemporalPrincipalArn` | The IAM principal that the Temporal server runs as. For Temporal Cloud, this is provided in your Namespace configuration. For self-hosted, this is the IAM identity of the server process (IAM user, EC2 instance role, EKS service account, etc.). | |
| 160 | +| `ExternalId` | A unique identifier that Temporal presents when assuming the role. For Temporal Cloud, this is provided in your Namespace configuration. For self-hosted, this is the value you configure in the compute provider settings. | |
| 161 | +| `LambdaFunctionArn` | The ARN of the Lambda function that Temporal will invoke. | |
| 162 | + |
| 163 | +<!-- |
| 164 | +TODO: Add the CloudFormation template once it's finalized. |
| 165 | +The template should create: |
| 166 | +- An IAM role with a trust policy scoped to TemporalPrincipalArn + ExternalId |
| 167 | +- A policy granting lambda:InvokeFunction and lambda:GetFunction on LambdaFunctionArn |
| 168 | +--> |
| 169 | + |
| 170 | +```yaml |
| 171 | +# CloudFormation template coming soon |
| 172 | +``` |
| 173 | + |
| 174 | +## 4. Create a Worker Deployment Version {#create-worker-deployment-version} |
| 175 | + |
| 176 | +Create a [Worker Deployment Version](/production-deployment/worker-deployments/worker-versioning) with a compute provider that points to your Lambda function. |
| 177 | +The compute configuration tells Temporal how to invoke your Worker: the provider type (`aws-lambda`), the Lambda function ARN, and the IAM role to assume. |
| 178 | +The deployment name and build ID must match the values in your Worker code. |
| 179 | + |
| 180 | +You can create the version using the Temporal UI, the Temporal CLI, or programmatically with an SDK. |
| 181 | + |
| 182 | +### Temporal UI |
| 183 | + |
| 184 | +Use the UI for one-off setup and exploration. |
| 185 | + |
| 186 | +When you create a version through the UI, the version is automatically set as current. |
| 187 | + |
| 188 | +<!-- TODO: Add UI instructions or screenshots --> |
| 189 | + |
| 190 | +### Temporal CLI |
| 191 | + |
| 192 | +Use the CLI for manual setup, shell scripts, and CI/CD pipelines. When you create a version through the CLI, you must [set the version as current](#set-current-version) as a separate step. |
| 193 | + |
| 194 | +<!-- |
| 195 | +TODO: Update this section once the CLI ships the `create-version` command with |
| 196 | +`--aws-lambda-invoke` and `--scaler-*` flags. |
| 197 | +--> |
| 198 | + |
| 199 | +```bash |
| 200 | +temporal worker deployment create-version \ |
| 201 | + --namespace <YOUR_NAMESPACE> \ |
| 202 | + --deployment-name my-app \ |
| 203 | + --build-id build-1 \ |
| 204 | + --aws-lambda-invoke arn:aws:lambda:<REGION>:<ACCOUNT_ID>:function:my-temporal-worker \ |
| 205 | + --scaler-min-instances 0 \ |
| 206 | + --scaler-max-instances 5 |
| 207 | +``` |
| 208 | + |
| 209 | +### SDK |
| 210 | + |
| 211 | +Use the SDK when the deployment registration is part of your application, for example a setup program that provisions infrastructure and registers the version together, or when you need to compute values dynamically. When you create a version through the SDK, you must [set the version as current](#set-current-version) as a separate step. |
| 212 | + |
| 213 | +<SdkTabs hideUnsupportedLanguages> |
| 214 | +<SdkTabs.Go> |
| 215 | + |
| 216 | +```go |
| 217 | +// Create the worker deployment |
| 218 | +client.CreateWorkerDeployment(ctx, &workflowservice.CreateWorkerDeploymentRequest{ |
| 219 | + Namespace: "default", |
| 220 | + DeploymentName: "my-app", |
| 221 | +}) |
| 222 | + |
| 223 | +// Create a version with Lambda compute config |
| 224 | +client.CreateWorkerDeploymentVersion(ctx, &workflowservice.CreateWorkerDeploymentVersionRequest{ |
| 225 | + Namespace: "default", |
| 226 | + DeploymentVersion: &deploymentpb.WorkerDeploymentVersion{ |
| 227 | + DeploymentName: "my-app", |
| 228 | + BuildId: "build-1", |
| 229 | + }, |
| 230 | + ComputeConfig: &computepb.ComputeConfig{ |
| 231 | + ScalingGroups: map[string]*computepb.ComputeConfigScalingGroup{ |
| 232 | + "default": { |
| 233 | + Provider: &computepb.ComputeProvider{ |
| 234 | + Type: "aws-lambda", |
| 235 | + Details: /* Lambda ARN, role ARN, external ID */, |
| 236 | + }, |
| 237 | + }, |
| 238 | + }, |
| 239 | + }, |
| 240 | +}) |
| 241 | +``` |
| 242 | + |
| 243 | +</SdkTabs.Go> |
| 244 | +</SdkTabs> |
| 245 | + |
| 246 | +## 5. Set the version as current {#set-current-version} |
| 247 | + |
| 248 | +If you created the version through the Temporal UI, the version is already current and you can skip this step. |
| 249 | + |
| 250 | +If you used the CLI or SDK, set the version as current. |
| 251 | +Without this step, tasks on the Task Queue will not route to the version, and Temporal will not invoke the Lambda function. |
| 252 | + |
| 253 | +```bash |
| 254 | +temporal worker deployment set-current-version \ |
| 255 | + --deployment-name my-app \ |
| 256 | + --build-id build-1 \ |
| 257 | + --ignore-missing-task-queues |
| 258 | +``` |
| 259 | + |
| 260 | +The `--ignore-missing-task-queues` flag is needed because no Worker has polled the Task Queue yet. |
| 261 | +With Serverless Workers, the Worker only connects when Temporal invokes the Lambda function. |
| 262 | + |
| 263 | +## 6. Verify the deployment {#verify-the-deployment} |
| 264 | + |
| 265 | +Start a Workflow on the same Task Queue to confirm that Temporal invokes your Lambda Worker. |
| 266 | + |
| 267 | +```bash |
| 268 | +temporal workflow start \ |
| 269 | + --task-queue my-task-queue \ |
| 270 | + --type MyWorkflow \ |
| 271 | + --input '"Hello, serverless!"' |
| 272 | +``` |
| 273 | + |
| 274 | +When the task lands on the Task Queue with no active pollers, Temporal detects the compute provider configuration and invokes your Lambda function. |
| 275 | +The Worker starts, connects to Temporal, picks up the task, and processes it. |
| 276 | + |
| 277 | +You can verify the invocation by checking: |
| 278 | + |
| 279 | +- **Temporal UI:** The Workflow execution should show task completions in the event history. |
| 280 | +- **AWS CloudWatch Logs:** The Lambda function's log group (`/aws/lambda/my-temporal-worker`) should show invocation logs with the Worker startup, task processing, and graceful shutdown. |
0 commit comments