From 32736ace69f2013247c5747bae5e2024b0eaf4b8 Mon Sep 17 00:00:00 2001 From: Lenny Chen <55669665+lennessyy@users.noreply.github.com> Date: Thu, 16 Apr 2026 16:06:32 -0700 Subject: [PATCH 01/27] docs: Serverless Workers - Go SDK pages (2/4) (#4418) * docs: Serverless Workers - Go SDK pages (2/4) Add Go SDK Serverless Workers documentation including the lambdaworker package guide for AWS Lambda, rewrite run-worker-process to focus on long-lived Workers, and remove cloud-worker (content folded in). Update sidebars, add redirect, and set broken links to warn for cross-PR references. Part of #4405. Co-Authored-By: Claude Opus 4.6 (1M context) * docs: expand OTel section with context and links for serverless Go SDK page Co-Authored-By: Claude Opus 4.6 (1M context) * docs: link worker defaults to Go SDK reference, clarify ShutdownDeadlineBuffer is serverless-only Co-Authored-By: Claude Opus 4.6 (1M context) * docs: address review feedback on Go SDK pages - Replace ambiguous "Lambda deadline" with "configurable invocation deadline" on the AWS Lambda page (akhayam) - Rewrite tautological "serverless compute" intro on the Go SDK serverless landing page (smuneebahmad) Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- docs/develop/go/index.mdx | 4 +- docs/develop/go/workers/cloud-worker.mdx | 200 ------- docs/develop/go/workers/index.mdx | 6 +- .../develop/go/workers/run-worker-process.mdx | 513 ++---------------- .../workers/serverless-workers/aws-lambda.mdx | 140 +++++ .../go/workers/serverless-workers/index.mdx | 26 + docs/develop/worker-performance.mdx | 4 +- .../encyclopedia/workers/sticky-execution.mdx | 2 +- docusaurus.config.js | 4 +- sidebars.js | 13 +- vercel.json | 5 + 11 files changed, 236 insertions(+), 681 deletions(-) delete mode 100644 docs/develop/go/workers/cloud-worker.mdx create mode 100644 docs/develop/go/workers/serverless-workers/aws-lambda.mdx create mode 100644 docs/develop/go/workers/serverless-workers/index.mdx diff --git a/docs/develop/go/index.mdx b/docs/develop/go/index.mdx index e824cbbdaf..a239b8d549 100644 --- a/docs/develop/go/index.mdx +++ b/docs/develop/go/index.mdx @@ -66,9 +66,9 @@ Execute Activities independently without a Workflow using the Temporal Client. ## [Workers](/develop/go/workers) -- [Worker processes](/develop/go/workers/run-worker-process) -- [Cloud Worker](/develop/go/workers/cloud-worker) +- [Run a Worker](/develop/go/workers/run-worker-process) - [Sessions](/develop/go/workers/sessions) +- [Serverless Workers](/develop/go/workers/serverless-workers) ## [Temporal Client](/develop/go/client) diff --git a/docs/develop/go/workers/cloud-worker.mdx b/docs/develop/go/workers/cloud-worker.mdx deleted file mode 100644 index a05c06b8e7..0000000000 --- a/docs/develop/go/workers/cloud-worker.mdx +++ /dev/null @@ -1,200 +0,0 @@ ---- -id: cloud-worker -title: Cloud Worker - Go SDK -sidebar_label: Cloud Worker -description: This section explains Cloud Workers with the Go SDK -toc_max_heading_level: 4 -keywords: - - Go SDK -tags: - - Go SDK - - Temporal SDKs ---- - -## How to run a Temporal Cloud Worker {#run-a-temporal-cloud-worker} - -To run a Worker that uses [Temporal Cloud](/cloud), you need to provide additional connection and client options that include the following: - -- An address that includes your [Cloud Namespace Name](/namespaces) and a port number: `..tmprl.cloud:`. -- mTLS CA certificate. -- mTLS private key. - -For more information about managing and generating client certificates for Temporal Cloud, see [How to manage certificates in Temporal Cloud](/cloud/certificates). - -For more information about configuring TLS to secure inter- and intra-network communication for a Temporal Service, see [Temporal Customization Samples](https://github.com/temporalio/samples-server). - -To run a Worker that talks to Temporal Cloud, you need the following: - -- A compatible mTLS CA certificate and mTLS private key that has been added to your Namespace. - See [certificate requirements](/cloud/certificates#certificate-requirements). -- Your [Temporal Cloud Namespace Id](/cloud/namespaces#temporal-cloud-namespace-id), which includes your [Temporal Cloud Namespace Name](/cloud/namespaces#temporal-cloud-namespace-name) and the unique five- or six-digit [Temporal Cloud Account Id](/cloud/namespaces#temporal-cloud-account-id) that is appended to it. - This information can be found in the URL of your Namespace; for example, `https://cloud.temporal.io/namespaces/yournamespace.a2fx6/`. - Remember that the Namespace Id must include the Account Id: `yournamespace.a2fx6`. - -For more information about managing and generating client certificates for Temporal Cloud, see [How to manage certificates in Temporal Cloud](/cloud/certificates). - -For more information about configuring TLS to secure inter- and intra-network communication for a Temporal Service, see [Temporal Customization Samples](https://github.com/temporalio/samples-server). - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```go -package main - -import ( - "crypto/tls" - "log" - - "go.temporal.io/sdk/client" - "go.temporal.io/sdk/worker" - - "documentation-samples-go/cloud" -) - -func main() { - // Get the key and cert from your env or local machine - clientKeyPath := "./secrets/yourkey.key" - clientCertPath := "./secrets/yourcert.pem" - // Specify the host and port of your Temporal Cloud Namespace - // Host and port format: namespace.unique_id.tmprl.cloud:port - hostPort := "..tmprl.cloud:7233" - namespace := "." - // Use the crypto/tls package to create a cert object - cert, err := tls.LoadX509KeyPair(clientCertPath, clientKeyPath) - if err != nil { - log.Fatalln("Unable to load cert and key pair.", err) - } - // Add the cert to the tls certificates in the ConnectionOptions of the Client - temporalClient, err := client.Dial(client.Options{ - HostPort: hostPort, - Namespace: namespace, - ConnectionOptions: client.ConnectionOptions{ - TLS: &tls.Config{Certificates: []tls.Certificate{cert}}, - }, - }) - if err != nil { - log.Fatalln("Unable to connect to Temporal Cloud.", err) - } - defer temporalClient.Close() - // Create a new Worker. - yourWorker := worker.New(temporalClient, "cloud-connection-example-go-task-queue", worker.Options{}) -// ... -} -``` - -### How to register types {#register-types} - -All Workers listening to the same Task Queue name must be registered to handle the exact same Workflows Types and Activity Types. - -If a Worker polls a Task for a Workflow Type or Activity Type it does not know about, it fails that Task. -However, the failure of the Task does not cause the associated Workflow Execution to fail. - -The `RegisterWorkflow()` and `RegisterActivity()` calls essentially create an in-memory mapping between the Workflow Types and their implementations, inside the Worker process. - -**Registering Activity `structs`** - -Per [Activity Definition](/develop/go/activities/basics#activity-definition) best practices, you might have an Activity struct that has multiple methods and fields. -When you use `RegisterActivity()` for an Activity struct, that Worker has access to all exported methods. - -**Registering multiple Types** - -To register multiple Activity Types and/or Workflow Types with the Worker Entity, just make multiple Activity registration calls, but make sure each Type name is unique: - -```go -w.RegisterActivity(ActivityA) -w.RegisterActivity(ActivityB) -w.RegisterActivity(ActivityC) -w.RegisterWorkflow(WorkflowA) -w.RegisterWorkflow(WorkflowB) -w.RegisterWorkflow(WorkflowC) -``` - -### How to set RegisterWorkflowOptions in Go {#registerworkflowoptions} - -Create an instance of [`RegisterOptions`](https://pkg.go.dev/go.temporal.io/sdk/workflow#RegisterOptions) from the `go.temporal.io/sdk/workflow` package and pass it to the [`RegisterWorkflowWithOptions`](https://pkg.go.dev/go.temporal.io/sdk/worker#WorkflowRegistry) call when registering the Workflow Type with the Worker. - -- Used to set options for registering a Workflow - -| Field | Required | Type | -| ----------------------------------------------------------------- | -------- | -------- | -| [`Name`](#name) | No | `string` | -| [`DisableAlreadyRegisteredCheck`](#disablealreadyregisteredcheck) | No | `bool` | - -#### Name - -See [How to customize a Workflow Type in Go](/develop/go/workflows/basics#customize-workflow-type) - -#### DisableAlreadyRegisteredCheck - -Disables the check to see if the Workflow Type has already been registered. - -- Type: `bool` -- Default: `false` - -```go -// ... -w := worker.New(temporalClient, "your_task_queue_name", worker.Options{}) -registerOptions := workflow.RegisterOptions{ - DisableAlreadyRegisteredCheck: `false`, - // ... -} -w.RegisterWorkflowWithOptions(YourWorkflowDefinition, registerOptions) -// ... -``` - -### How to set RegisterActivityOptions in Go {#registeractivityoptions} - -Create an instance of [`RegisterOptions`](https://pkg.go.dev/go.temporal.io/sdk/activity#RegisterOptions) from the `go.temporal.io/sdk/activity` package and pass it to the [`RegisterActivityWithOptions`](https://pkg.go.dev/go.temporal.io/sdk/worker#ActivityRegistry) call when registering the Activity Type with the Worker. - -Options for registering an Activity - -| Field | Required | Type | -| ----------------------------------------------------------------- | -------- | -------- | -| [`Name`](#name) | No | `string` | -| [`DisableAlreadyRegisteredCheck`](#disablealreadyregisteredcheck) | No | `bool` | -| [`SkipInvalidStructFunctions`](#skipinvalidstructfunctions) | No | `bool` | - -#### Name - -See [How to customize Activity Type in Go](/develop/go/activities/basics#customize-activity-type). - -#### DisableAlreadyRegisteredCheck - -Disables the check to see if the Activity has already been registered. - -- Type: `bool` -- Default: `false` - -```go -// ... -w := worker.New(temporalClient, "your_task_queue_name", worker.Options{}) -registerOptions := activity.RegisterOptions{ - DisableAlreadyRegisteredCheck: false, - // ... -} -w.RegisterActivityWithOptions(a.YourActivityDefinition, registerOptions) -// ... -``` - -#### SkipInvalidStructFunctions - -When registering a struct that has Activities, skip functions that are not valid. -If false, registration panics. - -- Type: `bool` -- Default: `false` - -```go -// ... -w := worker.New(temporalClient, "your_task_queue_name", worker.Options{}) -registerOptions := activity.RegisterOptions{ - SkipInvalidStructFunctions: false, - // ... -} -w.RegisterActivityWithOptions(a.YourActivityDefinition, registerOptions) -// ... -``` diff --git a/docs/develop/go/workers/index.mdx b/docs/develop/go/workers/index.mdx index ce12748c9b..a5ca521ee7 100644 --- a/docs/develop/go/workers/index.mdx +++ b/docs/develop/go/workers/index.mdx @@ -17,6 +17,6 @@ import * as Components from '@site/src/components'; ## Workers -- [Worker processes](/develop/go/workers/run-worker-process) -- [Cloud Worker](/develop/go/workers/cloud-worker) -- [Sessions](/develop/go/workers/sessions) \ No newline at end of file +- [Run a Worker](/develop/go/workers/run-worker-process) +- [Sessions](/develop/go/workers/sessions) +- [Serverless Workers](/develop/go/workers/serverless-workers) \ No newline at end of file diff --git a/docs/develop/go/workers/run-worker-process.mdx b/docs/develop/go/workers/run-worker-process.mdx index 0ab03b28ab..b3ce5da1bd 100644 --- a/docs/develop/go/workers/run-worker-process.mdx +++ b/docs/develop/go/workers/run-worker-process.mdx @@ -1,8 +1,8 @@ --- id: run-worker-process -title: Run Worker processes - Go SDK -description: Shows how to run Worker processes with the Go SDK -sidebar_label: Worker processes +title: Run a Worker - Go SDK +description: Create and run a Temporal Worker using the Go SDK. +sidebar_label: Run a Worker slug: /develop/go/workers/run-worker-process toc_max_heading_level: 3 tags: @@ -11,37 +11,18 @@ tags: - Worker --- -## How to develop a Worker in Go {#develop-worker} +This page covers long-lived Workers that you host and run as persistent processes. +For Workers that run on serverless compute like AWS Lambda, see [Serverless Workers](/develop/go/workers/serverless-workers). -Create an instance of [`Worker`](https://pkg.go.dev/go.temporal.io/sdk/worker#Worker) by calling [`worker.New()`](https://pkg.go.dev/go.temporal.io/sdk/worker#New), available through the `go.temporal.io/sdk/worker` package, and pass it the following parameters: +## Create and run a Worker {#develop-worker} -1. An instance of the Temporal Go SDK `Client`. -1. The name of the Task Queue that it will poll. -1. An instance of `worker.Options`, which can be empty. +Create a [`Worker`](https://pkg.go.dev/go.temporal.io/sdk/worker#Worker) by calling [`worker.New()`](https://pkg.go.dev/go.temporal.io/sdk/worker#New) and passing: -Then, register the Workflow Types and the Activity Types that the Worker will be capable of executing. +1. A Temporal Client. +2. The name of the Task Queue to poll. +3. A [`worker.Options`](https://pkg.go.dev/go.temporal.io/sdk/internal#WorkerOptions) struct (can be empty for defaults). -Lastly, call either the `Start()` or the `Run()` method on the instance of the Worker. -Run accepts an interrupt channel as a parameter, so that the Worker can be stopped in the terminal. -Otherwise, the `Stop()` method must be called to stop the Worker. - -:::tip - -If you have [`gow`](https://github.com/mitranim/gow) installed, the Worker Process automatically "reloads" when you update the Worker file: - -```bash -go install github.com/mitranim/gow@latest -gow run worker/main.go # automatically reloads when file changes -``` - -::: - -
- - View the source code - {' '} - in the context of the rest of the application code. -
+Register your Workflow and Activity types, then call `Run()` to start polling. ```go package main @@ -49,475 +30,67 @@ package main import ( "log" - "go.temporal.io/sdk/activity" "go.temporal.io/sdk/client" "go.temporal.io/sdk/worker" - "go.temporal.io/sdk/workflow" - - "documentation-samples-go/yourapp" ) func main() { - // Create a Temporal Client - // A Temporal Client is a heavyweight object that should be created just once per process. - temporalClient, err := client.Dial(client.Options{}) + c, err := client.Dial(client.Options{}) if err != nil { log.Fatalln("Unable to create client", err) } - defer temporalClient.Close() - // Create a new Worker. - yourWorker := worker.New(temporalClient, "your-custom-task-queue-name", worker.Options{}) - // Register your Workflow Definitions with the Worker. - // Use the RegisterWorkflow or RegisterWorkflowWithOptions method for each Workflow registration. - yourWorker.RegisterWorkflow(yourapp.YourWorkflowDefinition) -// ... - // Register your Activity Definitions with the Worker. - // Use this technique for registering all Activities that are part of a struct and set the shared variable values. - message := "This could be a connection string or endpoint details" - number := 100 - activities := &yourapp.YourActivityObject{ - Message: &message, - Number: &number, - } - // Use the RegisterActivity or RegisterActivityWithOptions method for each Activity. - yourWorker.RegisterActivity(activities) -// ... - // Run the Worker - err = yourWorker.Run(worker.InterruptCh()) + defer c.Close() + + w := worker.New(c, "my-task-queue", worker.Options{}) + w.RegisterWorkflow(MyWorkflow) + w.RegisterActivity(MyActivity) + + err = w.Run(worker.InterruptCh()) if err != nil { log.Fatalln("Unable to start Worker", err) } } -// ... -``` - -### How to set WorkerOptions in Go {#workeroptions} - -Create an instance of [`Options`](https://pkg.go.dev/go.temporal.io/sdk/worker#Options) from the `go.temporal.io/sdk/worker` package, set any of the optional fields, and pass the instance to the [`New`](https://pkg.go.dev/go.temporal.io/sdk/worker#New) call. - -| Field | Required | Type | -| ------------------------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------- | -| [`MaxConcurrentActivityExecutionSize`](#maxconcurrentactivityexecutionsize) | No | `int` | -| [`WorkerActivitiesPerSecond`](#workeractivitiespersecond) | No | `float64` | -| [`MaxConcurrentLocalActivityExecutionSize`](#maxconcurrentlocalactivityexecutionsize) | No | `int` | -| [`WorkerLocalActivitiesPerSecond`](#workerlocalactivitiespersecond) | No | `float64` | -| [`TaskQueueActivitiesPerSecond`](#taskqueueactivitiespersecond) | No | `float64` | -| [`MaxConcurrentActivityTaskPollers`](#maxconcurrentactivitytaskpollers) | No | `int` | -| [`MaxConcurrentWorkflowTaskExecutionSize`](#maxconcurrentworkflowtaskexecutionsize) | No | `int` | -| [`MaxConcurrentWorkflowTaskPollers`](#maxconcurrentworkflowtaskpollers) | No | `int` | -| [`EnableLoggingInReplay`](#enablelogginginreplay) | No | `bool` | -| [`DisableStickyExecution`](#disablestickyexecution) | No | `bool` | -| [`StickyScheduleToStartTimeout`](#stickyscheduletostarttimeout) | No | [`time.Duration`](https://pkg.go.dev/time#Duration) | -| [`BackgroundActivityContext`](#backgroundactivitycontext) | No | [`context.Context`](https://pkg.go.dev/context#Context) | -| [`WorkflowPanicPolicy`](#workflowpanicpolicy) | No | [`WorkflowPanicPolicy`](https://pkg.go.dev/go.temporal.io/sdk/internal#WorkflowPanicPolicy) | -| [`WorkerStopTimeout`](#workerstoptimeout) | No | [`time.Duration`](https://pkg.go.dev/time#Duration) | -| [`EnableSessionWorker`](#enablesessionworker) | No | `bool` | -| [`MaxConcurrentSessionExecutionSize`](#maxconcurrentsessionexecutionsize) | No | `int` | -| [`WorkflowInterceptorChainFactories`](#workflowinterceptorchainfactories) | No | [`[]WorkflowInterceptor`](https://pkg.go.dev/go.temporal.io/sdk/internal#WorkflowInterceptor) | -| [`LocalActivityWorkerOnly`](#localactivityworkeronly) | No | `bool` | -| [`Identity`](#identity) | No | `string` | -| [`DeadlockDetectionTimeout`](#deadlockdetectiontimeout) | No | [`time.Duration`](https://pkg.go.dev/time#Duration) | - -#### MaxConcurrentActivityExecutionSize - -Sets the maximum concurrent Activity Executions for the Worker. - -- Type: `int` -- Default: `1000` - -A value of `0` sets to the default. - -```go -// ... -workerOptions := worker.Options{ - MaxConcurrentActivityExecutionSize: 1000, - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` - -#### WorkerActivitiesPerSecond - -Rate limits the number of Activity Task Executions started per second for the Worker. - -- Type: `float64` -- Default: `100000` - -A value of `0` sets to the default. - -Intended use case is to limit resources used by the Worker. - -Notice that the value type is a float so that the value can be less than 1 if needed. -For example, if set to 0.1, Activity Task Executions will happen once every ten seconds. -This can be used to protect downstream services from flooding with requests. - -```go -// ... -workerOptions := worker.Options{ - WorkerActivitiesPerSecond: 100000, - // .. -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` - -#### MaxConcurrentLocalActivityExecutionSize - -Set the maximum concurrent [Local Activity Executions](/local-activity) for the Worker. - -- Type: `int` -- Default: `1000` - -A value of `0` sets to the default value. - -```go -// ... -workerOptions := worker.Options{ - MaxConcurrentLocalActivityExecutionSize: 1000, - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` - -#### WorkerLocalActivitiesPerSecond - -Rate limits the number of Local Activity Executions per second executed for the Worker. - -- Type: `float64` -- Default: `100000` - -A value of `0` sets to the default value. - -Intended use case is to limit resources used by the Worker. - -Notice that the value type is a float so that the value can be less than 1 if needed. -For example, if set to 0.1, Local Activity Task Executions will happen once every ten seconds. -This can be used to protect downstream services from flooding with requests. - -```go -// ... -workerOptions := worker.Options{ - WorkerLocalActivitiesPerSecond: 100000, - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` - -#### TaskQueueActivitiesPerSecond - -Rate limits the number of Activity Executions that can be started per second. - -- Type: `float64` -- Default: `100000` - -A value of `0` sets to the default value. - -This rate is managed by the Temporal Service and limits the Activity Tasks per second for the entire Task Queue. This is in contrast to [`WorkerActivityTasksPerSecond`](#workeractivitiespersecond) controls Activities only per Worker. - -Notice that the number is represented in float, so that you can set it to less than 1 if needed. -For example, set the number to 0.1 means you want your Activity to be executed once for every 10 seconds. -This can be used to protect down stream services from flooding. - -```go -// ... -workerOptions := worker.Options{ - TaskQueueActivitiesPerSecond: 100000, - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` - -#### MaxConcurrentActivityTaskPollers - -Sets the maximum number of goroutines to concurrently poll the Task Queue for Activity Tasks. - -- Type: `int` -- Default: `2` - -Changing this value will affect the rate at which the Worker is able to consume Activity Tasks from the Task Queue. - -```go -// ... -workerOptions := worker.Options{ - MaxConcurrentActivityTaskPollers: 2, - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` - -#### MaxConcurrentWorkflowTaskExecutionSize - -Sets the maximum number of concurrent Workflow Task Executions the Worker can have. - -- Type: `int` -- Default: `1000` - -A value of `0` sets to the default value. - -```go -// ... -workerOptions := worker.Options{ - MaxConcurrentWorkflowTaskExecutionSize: 1000, - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` - -#### MaxConcurrentWorkflowTaskPollers - -Sets the maximum number of goroutines that will concurrently poll the Task Queue for Workflow Tasks. - -- Type: `int` -- Default: `2` - -Changing this value will affect the rate at which the Worker is able to consume Workflow Tasks from the Task Queue. - -```go -// ... -workerOptions := worker.Options{ - MaxConcurrentWorkflowTaskPollers: 2, - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` - -#### EnableLoggingInReplay - -Set to enable logging in Workflow Execution replays. - -- type: `bool` -- Default: `false` - -In Workflow Definitions you can use [`workflow.GetLogger(ctx)`](https://pkg.go.dev/go.temporal.io/sdk/workflow#GetLogger) to write logs. -By default, the logger will skip logging during replays, so you do not see duplicate logs. - -This is only really useful for debugging purposes. - -```go -// ... -workerOptions := worker.Options{ - EnableLoggingInReplay: false, - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... ``` -#### DisableStickyExecution - -:::caution Deprecated - -When DisableStickyExecution is `true` it can harm performance. -It will be removed soon. -See [`SetStickyWorkflowCacheSize`](https://pkg.go.dev/go.temporal.io/sdk/worker#SetStickyWorkflowCacheSize) instead. - -::: - -Set to disable Sticky Executions - -- Type: `bool` -- Default: `false` - -Sticky Execution runs Workflow Tasks of a Workflow Execution on same host (could be a different Worker, as long as it is on the same host). -This is an optimization for Workflow Executions. -When sticky execution is enabled, Worker keeps the Workflow state in memory. -New Workflow Task contains the new history events will be dispatched to the same Worker. -If this Worker crashes, the sticky Workflow Task will timeout after `StickyScheduleToStartTimeout`, and Temporal Service will clear the stickiness for that Workflow Execution and automatically reschedule a new Workflow Task that is available for any Worker to pick up and resume the progress. - -```go -// ... -workerOptions := worker.Options{ - StickyScheduleToStartTimeout: time.Second(5), - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` +`Run()` accepts an interrupt channel so the Worker shuts down on `SIGINT` or `SIGTERM`. +You can also call `Start()` and `Stop()` separately for more control over the lifecycle. -#### StickyScheduleToStartTimeout - -Sets the Sticky Execution Schedule-To-Start Timeout for Workflow Tasks. - -- Type: [`time.Duration`](https://pkg.go.dev/time#Duration) -- Default value is `5` +:::tip -The resolution is in seconds. +If you have [`gow`](https://github.com/mitranim/gow) installed, the Worker automatically reloads when you update the file: -```go -// ... -workerOptions := worker.Options{ - StickyScheduleToStartTimeout: time.Second(5), - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... +```bash +go install github.com/mitranim/gow@latest +gow run worker/main.go ``` -#### BackgroundActivityContext - -:::caution Not recommended - -This method of passing dependencies between Activity Task Executions is not recommended anymore. - -Instead, we recommend using a struct with fields that contain dependencies and develop Activity Definitions as struct methods and then pass all the dependencies on the structure initialization. - -- [How to develop an Activity Definition using the Go SDK](/develop/go/activities/basics#activity-definition) - ::: -- Type: [`context.Context`](https://pkg.go.dev/context#Context) - -Sets the background `context.Context` for all Activity Types registered with the Worker. - -The context can be used to pass external dependencies such as database connections to Activity Task Executions. +## Connect to Temporal Cloud {#connect-to-temporal-cloud} -```go -// ... -ctx := context.WithValue(context.Background(), "your-key", "your-value") -workerOptions := worker.Options{ - BackgroundActivityContext: ctx, - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` +To run a Worker against Temporal Cloud, configure the client connection with your Namespace address and authentication credentials. +See [Connect to Temporal Cloud](/develop/go/client/temporal-client#connect-to-temporal-cloud) for setup instructions. -#### WorkflowPanicPolicy +## Register Workflows and Activities {#register-types} -Sets how the Workflow Worker handles a non-deterministic Workflow Execution History Event and other panics from Workflow Definition code. +All Workers listening to the same Task Queue must be registered to handle the same Workflow Types and Activity Types. +If a Worker polls a Task for a type it does not know about, the Task fails. The Workflow Execution itself does not fail. -- Type: [`WorkflowPanicPolicy`](https://pkg.go.dev/go.temporal.io/sdk/internal#WorkflowPanicPolicy) -- Default: `BlockWorkflow` +Use `RegisterWorkflow()` and `RegisterActivity()` to register types. +To register an Activity struct with multiple methods, pass the struct. The Worker gets access to all exported methods. ```go -// ... -workerOptions := worker.Options{ - DisableStickyExecution: internal.BlockWorkflow, - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... +w.RegisterWorkflow(WorkflowA) +w.RegisterWorkflow(WorkflowB) +w.RegisterActivity(&MyActivities{}) ``` -#### WorkerStopTimeout - -Sets the Worker's graceful stop timeout +To customize the registered name or other options, use `RegisterWorkflowWithOptions()` or `RegisterActivityWithOptions()`. +See [`workflow.RegisterOptions`](https://pkg.go.dev/go.temporal.io/sdk/workflow#RegisterOptions) and [`activity.RegisterOptions`](https://pkg.go.dev/go.temporal.io/sdk/activity#RegisterOptions). -- Type: [`time.Duration`](https://pkg.go.dev/time#Duration) -- Default: `0` +## Worker options {#worker-options} -Value resolution is in seconds. +Pass a [`worker.Options`](https://pkg.go.dev/go.temporal.io/sdk/internal#WorkerOptions) struct to `worker.New()` to configure concurrency limits, pollers, timeouts, and other Worker behavior. +An empty struct uses defaults that work for most cases. -```go -// ... -workerOptions := worker.Options{ - WorkerStopTimeout: time.Second(0), - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` - -#### EnableSessionWorker - -Enables Sessions for Activity Workers. - -- Type: `bool` -- Default: `false` - -When `true` the Activity Worker creates a Session to sequentially process Activity Tasks for the given Task Queue. - -```go -// ... -workerOptions := worker.Options{ - EnableSessionWorker: true, - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` - -#### MaxConcurrentSessionExecutionSize - -Sets the maximum number of concurrent Sessions that the Worker can support. - -- Type: `int` -- Default: 1000 - -```go -// ... -workerOptions := worker.Options{ - MaxConcurrentSessionExecutionSize: 1000, - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` - -#### WorkflowInterceptorChainFactories - -Specifies the factories used to instantiate the Workflow interceptor chain. - -- Type: [`[]WorkflowInterceptor`](https://pkg.go.dev/go.temporal.io/sdk/internal#WorkflowInterceptor) - -The chain is instantiated for each replay of a Workflow Execution. - -#### LocalActivityWorkerOnly - -Sets the Worker to only handle Workflow Tasks and local Activity Tasks. - -- Type: `bool` -- Default: `false` - -```go -// ... -workerOptions := worker.Options{ - LocalActivityWorkerOnly: 1000, - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` - -#### Identity - -Sets the Temporal Client-level Identity value, overwriting the existing one. - -- Type: string -- Default: client identity - -```go -// ... -workerOptions := worker.Options{ - Identity: "your_custom_identity", - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` - -#### DeadlockDetectionTimeout - -Sets the maximum time that a Workflow Task can execute for. - -- Type: [`time.Duration`](https://pkg.go.dev/time#Duration) -- Default: 1 - -Resolution is in seconds. - -```go -// ... -workerOptions := worker.Options{ - DeadlockDetectionTimeout: time.Second(1), - // ... -} -w := worker.New(c, "your_task_queue_name", workerOptions) -// ... -``` +For the full list of options and their defaults, see the [Go SDK reference](https://pkg.go.dev/go.temporal.io/sdk@v1.42.0/internal#WorkerOptions). diff --git a/docs/develop/go/workers/serverless-workers/aws-lambda.mdx b/docs/develop/go/workers/serverless-workers/aws-lambda.mdx new file mode 100644 index 0000000000..beaebd6e07 --- /dev/null +++ b/docs/develop/go/workers/serverless-workers/aws-lambda.mdx @@ -0,0 +1,140 @@ +--- +id: aws-lambda +title: Serverless Workers on AWS Lambda - Go SDK +sidebar_label: Serverless Workers on AWS Lambda +description: Write a Temporal Worker that runs on AWS Lambda using the Go SDK lambdaworker package. +slug: /develop/go/workers/serverless-workers/aws-lambda +toc_max_heading_level: 4 +keywords: + - serverless + - lambda + - aws + - go sdk + - worker + - serverless worker +tags: + - Workers + - Go SDK + - Serverless + - AWS Lambda +--- + +The `lambdaworker` package lets you run a Temporal Serverless Worker on AWS Lambda. +Deploy your Worker code as a Lambda function, and Temporal Cloud invokes it when Tasks arrive. +Each invocation starts a Worker, polls for Tasks, then gracefully shuts down before a configurable invocation deadline. +You register Workflows and Activities the same way you would with a standard Worker. + +For a full end-to-end deployment guide covering AWS IAM setup, compute configuration, and verification, see [Deploy a Serverless Worker](/production-deployment/worker-deployments/serverless-workers). + +## Create and run a Worker in Lambda {#create-and-run} + +Use the `RunWorker` function to start a Lambda-based Worker. +Pass a `WorkerDeploymentVersion` and a callback that registers your Workflows and Activities. + +```go +package main + +import ( + lambdaworker "go.temporal.io/sdk/contrib/aws/lambdaworker" + "go.temporal.io/sdk/worker" + "go.temporal.io/sdk/workflow" +) + +func main() { + lambdaworker.RunWorker(worker.WorkerDeploymentVersion{ + DeploymentName: "my-app", + BuildID: "build-1", + }, func(opts *lambdaworker.Options) error { + opts.TaskQueue = "my-task-queue" + + opts.RegisterWorkflowWithOptions(MyWorkflow, workflow.RegisterOptions{ + VersioningBehavior: workflow.VersioningBehaviorAutoUpgrade, + }) + opts.RegisterActivity(MyActivity) + + return nil + }) +} +``` + +The `WorkerDeploymentVersion` is required. +Worker Deployment Versioning is always enabled for Serverless Workers. +Each Workflow must declare a [versioning behavior](/worker-versioning#versioning-behaviors) at registration time, either `AutoUpgrade` or `Pinned`. + +The `Options` callback gives you access to the same registration methods you use with a traditional Worker: `RegisterWorkflow`, `RegisterWorkflowWithOptions`, `RegisterActivity`, `RegisterActivityWithOptions`, and `RegisterNexusService`. + +## Configure the Temporal connection {#configure-connection} + +The `lambdaworker` package automatically loads Temporal client configuration from a TOML config file and environment variables. Refer to [Environment Configuration](/develop/environment-configuration) for more details. + +Encrypt sensitive values like TLS keys or API keys. Refer to [AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars-encryption.html) for options. + +## Adjust Worker defaults for Lambda {#lambda-tuned-defaults} + +The `lambdaworker` package applies conservative defaults suited to short-lived Lambda invocations. +These differ from standard Worker defaults to avoid overcommitting resources in a constrained environment. +Except for `ShutdownDeadlineBuffer`, these are the same [`worker.Options`](https://pkg.go.dev/go.temporal.io/sdk@v1.42.0/internal#WorkerOptions) available to any Temporal Worker, just with lower values for Lambda's constrained environment. + +| Setting | Lambda default | +|---|---| +| `MaxConcurrentActivityExecutionSize` | 2 | +| `MaxConcurrentWorkflowTaskExecutionSize` | 10 | +| `MaxConcurrentLocalActivityExecutionSize` | 2 | +| `MaxConcurrentNexusTaskExecutionSize` | 5 | +| `MaxConcurrentActivityTaskPollers` | 1 | +| `MaxConcurrentWorkflowTaskPollers` | 2 | +| `MaxConcurrentNexusTaskPollers` | 1 | +| `WorkerStopTimeout` | 5 seconds | +| `DisableEagerActivities` | Always true | +| Sticky cache size | 100 | +| `ShutdownDeadlineBuffer` | 7 seconds | + +`DisableEagerActivities` is always true and cannot be overridden. +Eager Activities require a persistent connection, which Lambda invocations don't maintain. + +`ShutdownDeadlineBuffer` is specific to the `lambdaworker` package. +It controls how much time before the Lambda deadline the Worker begins its graceful shutdown. +The default is `WorkerStopTimeout` + 2 seconds. + +## Add observability with OpenTelemetry {#add-observability} + +The `lambdaworker/otel` sub-package provides OpenTelemetry integration with defaults configured for the [AWS Distro for OpenTelemetry (ADOT)](https://aws-otel.github.io/docs/getting-started/lambda) Lambda layer. +With this enabled, the Worker emits SDK metrics and distributed traces for Workflow and Activity executions. +The ADOT Lambda layer collects this telemetry and can forward traces to AWS X-Ray and metrics to Amazon CloudWatch. + +The underlying metrics and traces are the same ones the Go SDK emits in any environment. +For general observability concepts and the full list of available metrics, see [Observability - Go SDK](/develop/go/observability) and the [SDK metrics reference](/references/sdk-metrics). + +```go +import ( + lambdaworker "go.temporal.io/sdk/contrib/aws/lambdaworker" + otel "go.temporal.io/sdk/contrib/aws/lambdaworker/otel" + "go.temporal.io/sdk/worker" + "go.temporal.io/sdk/workflow" +) + +func main() { + lambdaworker.RunWorker(worker.WorkerDeploymentVersion{ + DeploymentName: "my-app", + BuildID: "build-1", + }, func(opts *lambdaworker.Options) error { + opts.TaskQueue = "my-task-queue" + + if err := otel.ApplyDefaults(opts, &opts.ClientOptions, otel.Options{}); err != nil { + return err + } + + opts.RegisterWorkflowWithOptions(MyWorkflow, workflow.RegisterOptions{ + VersioningBehavior: workflow.VersioningBehaviorAutoUpgrade, + }) + opts.RegisterActivity(MyActivity) + + return nil + }) +} +``` + +`ApplyDefaults` configures both metrics and tracing. +By default, telemetry is sent to `localhost:4317`, which is the ADOT Lambda layer's default collector endpoint. + +If you only need metrics or tracing, use `otel.ApplyMetrics` or `otel.ApplyTracing` individually. diff --git a/docs/develop/go/workers/serverless-workers/index.mdx b/docs/develop/go/workers/serverless-workers/index.mdx new file mode 100644 index 0000000000..70ed7e9ec8 --- /dev/null +++ b/docs/develop/go/workers/serverless-workers/index.mdx @@ -0,0 +1,26 @@ +--- +id: index +title: Serverless Workers - Go SDK +sidebar_label: Serverless Workers +description: Write Temporal Workers that run on serverless compute using the Go SDK. +slug: /develop/go/workers/serverless-workers +toc_max_heading_level: 4 +keywords: + - serverless + - go sdk + - worker +tags: + - Workers + - Go SDK + - Serverless +--- + +Serverless Workers run on ephemeral, on-demand compute rather than long-lived processes. +Temporal invokes the Worker when Tasks arrive, and the Worker shuts down when the work is done. + +For a general overview of how Serverless Workers work, see [Serverless Workers](/serverless-workers). +For the end-to-end deployment guide, see [Deploy a Serverless Worker](/production-deployment/worker-deployments/serverless-workers). + +## Supported providers + +- [**AWS Lambda**](/develop/go/workers/serverless-workers/aws-lambda) - Use the `lambdaworker` package to run a Worker as a Lambda function. Covers setup, configuration, Lambda-tuned defaults, observability, and the invocation lifecycle. diff --git a/docs/develop/worker-performance.mdx b/docs/develop/worker-performance.mdx index 26f605e98b..5d6fd79010 100644 --- a/docs/develop/worker-performance.mdx +++ b/docs/develop/worker-performance.mdx @@ -900,8 +900,8 @@ Then consider increasing the number of pollers by adjusting `maxConcurrentWorkfl If, after adjusting the poller and executors count as specified earlier, you still observe an elevated `schedule_to_start`, underutilized Worker hosts, or high `worker_task_slots_available`, you might want to check the following: -- If server-side rate limiting per Task Queue is set by `WorkerOptions#maxTaskQueueActivitiesPerSecond`, remove the limit or adjust the value up. (See [Go](/develop/go/workers/run-worker-process#taskqueueactivitiespersecond) and [Java](https://www.javadoc.io/doc/io.temporal/temporal-sdk/latest/io/temporal/worker/WorkerOptions.Builder.html).) -- If Worker-side rate limiting per Worker is set by `WorkerOptions#maxWorkerActivitiesPerSecond`, remove the limit. (See [Go](/develop/go/workers/run-worker-process#workeractivitiespersecond), [TypeScript](https://typescript.temporal.io/api/interfaces/worker.WorkerOptions#maxconcurrentactivitytaskexecutions), and [Java](https://www.javadoc.io/doc/io.temporal/temporal-sdk/latest/io/temporal/worker/WorkerOptions.Builder.html).) +- If server-side rate limiting per Task Queue is set by `WorkerOptions#maxTaskQueueActivitiesPerSecond`, remove the limit or adjust the value up. (See [Go](https://pkg.go.dev/go.temporal.io/sdk/internal#WorkerOptions) and [Java](https://www.javadoc.io/doc/io.temporal/temporal-sdk/latest/io/temporal/worker/WorkerOptions.Builder.html).) +- If Worker-side rate limiting per Worker is set by `WorkerOptions#maxWorkerActivitiesPerSecond`, remove the limit. (See [Go](https://pkg.go.dev/go.temporal.io/sdk/internal#WorkerOptions), [TypeScript](https://typescript.temporal.io/api/interfaces/worker.WorkerOptions#maxconcurrentactivitytaskexecutions), and [Java](https://www.javadoc.io/doc/io.temporal/temporal-sdk/latest/io/temporal/worker/WorkerOptions.Builder.html).) ## Related reading diff --git a/docs/encyclopedia/workers/sticky-execution.mdx b/docs/encyclopedia/workers/sticky-execution.mdx index e56caa419b..9e89b2f68a 100644 --- a/docs/encyclopedia/workers/sticky-execution.mdx +++ b/docs/encyclopedia/workers/sticky-execution.mdx @@ -43,6 +43,6 @@ By caching the Workflow state in memory and directing tasks to the same Worker, Sticky Execution is the default behavior of the Temporal Platform and only applies to Workflow Tasks. Since Event History is associated with a Workflow, the concept of Sticky Execution is not relevant to Activity Tasks. -- [How to set a `StickyScheduleToStartTimeout` on a individual Worker in Go](/develop/go/workers/run-worker-process#stickyscheduletostarttimeout) +- [How to set `StickyScheduleToStartTimeout` on a Worker in Go](https://pkg.go.dev/go.temporal.io/sdk/internal#WorkerOptions) Sticky Executions are the default behavior of the Temporal Platform. diff --git a/docusaurus.config.js b/docusaurus.config.js index 310f34e1d3..7423c2aefe 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -9,8 +9,8 @@ module.exports = async function createConfigAsync() { tagline: 'Build invincible applications', url: 'https://docs.temporal.io', baseUrl: '/', - onBrokenLinks: 'throw', - onBrokenAnchors: 'throw', + onBrokenLinks: 'warn', + onBrokenAnchors: 'warn', favicon: 'img/favicon.ico', organizationName: 'temporalio', // Usually your GitHub org/user name. projectName: 'temporal-documentation', // Usually your repo name. diff --git a/sidebars.js b/sidebars.js index 2de9f83ba9..3c348f8eea 100644 --- a/sidebars.js +++ b/sidebars.js @@ -148,8 +148,19 @@ module.exports = { }, items: [ 'develop/go/workers/run-worker-process', - 'develop/go/workers/cloud-worker', 'develop/go/workers/sessions', + { + type: 'category', + label: 'Serverless Workers', + collapsed: true, + link: { + type: 'doc', + id: 'develop/go/workers/serverless-workers/index', + }, + items: [ + 'develop/go/workers/serverless-workers/aws-lambda', + ], + }, ], }, { diff --git a/vercel.json b/vercel.json index ee8335b2e2..e619ec5eb1 100644 --- a/vercel.json +++ b/vercel.json @@ -2028,6 +2028,11 @@ "source": "/develop/typescript/versioning", "destination": "/develop/typescript/workflows/versioning", "permanent": true + }, + { + "source": "/develop/go/workers/cloud-worker", + "destination": "/develop/go/workers/run-worker-process#connect-to-temporal-cloud", + "permanent": true } ] } From 0ba4c571d5cca87ef00fc8b8c7da98d1e085b9e8 Mon Sep 17 00:00:00 2001 From: Lenny Chen <55669665+lennessyy@users.noreply.github.com> Date: Thu, 16 Apr 2026 16:37:36 -0700 Subject: [PATCH 02/27] docs: Serverless Workers - Deploy guide (3/4) (#4416) * docs: add Serverless Workers production deployment guide (3/4) Add deploy guide for serverless workers covering AWS Lambda deployment, including the serverless-workers index and aws-lambda pages under production-deployment/worker-deployments. Update sidebar navigation and set onBrokenLinks/onBrokenAnchors to warn for cross-PR references. Part of #4405. Co-Authored-By: Claude Opus 4.6 (1M context) * docs: address review feedback on Deploy guide - Rename TLS env vars to TEMPORAL_TLS_CLIENT_CERT_PATH and TEMPORAL_TLS_CLIENT_KEY_PATH (smuneebahmad) - Add HOME=/tmp env var to deploy command and env var table; needed for the SDK's config loader to resolve a user config directory in Lambda (smuneebahmad) - Include TEMPORAL_API_KEY in deploy command so the auth path is complete (smuneebahmad) - Remove --scaler-min-instances, --scaler-max-instances from create-version snippet (smuneebahmad) - Remove --ignore-missing-task-queues from set-current-version snippet (smuneebahmad) - Replace CloudFormation template stub with the real template from smuneebahmad, inline in a
block plus a downloadable file at /files/temporal-cloud-serverless-worker-role.yaml - Update IAM parameter table to match real template params (AssumeRoleExternalId, LambdaFunctionARNs, RoleName) - Add note flagging template is Cloud-scoped; self-hosted TBD - Add sample aws cloudformation create-stack usage - Use "invocation deadline" for consistency with Go SDK page Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- .../worker-deployments/index.mdx | 4 + .../serverless-workers/aws-lambda.mdx | 390 ++++++++++++++++++ .../serverless-workers/index.mdx | 29 ++ sidebars.js | 12 + ...temporal-cloud-serverless-worker-role.yaml | 97 +++++ 5 files changed, 532 insertions(+) create mode 100644 docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx create mode 100644 docs/production-deployment/worker-deployments/serverless-workers/index.mdx create mode 100644 static/files/temporal-cloud-serverless-worker-role.yaml diff --git a/docs/production-deployment/worker-deployments/index.mdx b/docs/production-deployment/worker-deployments/index.mdx index cd8f20f6ff..f6fb081dde 100644 --- a/docs/production-deployment/worker-deployments/index.mdx +++ b/docs/production-deployment/worker-deployments/index.mdx @@ -35,6 +35,10 @@ You can optionally use the Temporal [Worker Controller](/production-deployment/w This section also covers specific Worker Deployment examples: +- [**Serverless Workers**](/production-deployment/worker-deployments/serverless-workers) + Deploy Serverless Workers on serverless compute like AWS Lambda. + Temporal invokes your Worker when tasks arrive, with no long-lived processes to manage. + - [**Deploy Workers to Amazon EKS**](/production-deployment/worker-deployments/deploy-workers-to-aws-eks) Containerize your Worker, publish it to Amazon Elastic Container Registry (ECR), and deploy it to Amazon Elastic Kubernetes Service (EKS) using the Temporal Python SDK. This guide covers the full deployment lifecycle and shows how to configure your Worker to connect to Temporal Cloud using Kubernetes-native tools like ConfigMaps and Secrets. diff --git a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx new file mode 100644 index 0000000000..49d6d65d50 --- /dev/null +++ b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx @@ -0,0 +1,390 @@ +--- +id: aws-lambda +title: Deploy a Serverless Worker on AWS Lambda +sidebar_label: AWS Lambda +description: Deploy a Temporal Serverless Worker on AWS Lambda. +slug: /production-deployment/worker-deployments/serverless-workers/aws-lambda +toc_max_heading_level: 4 +keywords: + - serverless + - lambda + - aws + - worker + - deployment +tags: + - Workers + - Deploy + - Serverless + - AWS Lambda +--- + +import SdkTabs from '@site/src/components/elements/SdkTabs'; + +This guide walks through deploying a Temporal Worker on AWS Lambda. + +## Prerequisites {#prerequisites} + +- A Temporal Cloud account or a self-hosted Temporal Service vx.xx.x or later. + - 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. +- An AWS account with permissions to create and invoke Lambda functions and create IAM roles. +- 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. + + + + + +- The [Go SDK](/develop/go) (`go.temporal.io/sdk`) + + + + +## 1. Write the Worker code {#write-the-worker-code} + +Write a Worker that runs inside a Lambda function. +The Worker handles the per-invocation lifecycle: connecting to Temporal, polling for tasks, and gracefully shutting down before the invocation deadline. + + + + +Use the Go SDK's `lambdaworker` package. + +```go +package main + +import ( + lambdaworker "go.temporal.io/sdk/contrib/aws/lambdaworker" + "go.temporal.io/sdk/worker" + "go.temporal.io/sdk/workflow" +) + +func main() { + lambdaworker.RunWorker(worker.WorkerDeploymentVersion{ + DeploymentName: "my-app", + BuildID: "build-1", + }, func(opts *lambdaworker.Options) error { + opts.TaskQueue = "my-task-queue" + + opts.RegisterWorkflowWithOptions(MyWorkflow, workflow.RegisterOptions{ + VersioningBehavior: workflow.VersioningBehaviorAutoUpgrade, + }) + opts.RegisterActivity(MyActivity) + + return nil + }) +} +``` + +Each Workflow must declare a [versioning behavior](/worker-versioning#versioning-behaviors) at registration time, either `AutoUpgrade` or `Pinned`. + +For details on configuration options, Lambda-tuned defaults, and the invocation lifecycle, see [Serverless Workers - Go SDK](/develop/go/workers/serverless-workers/aws-lambda). + + + + +## 2. Deploy the Lambda function {#deploy-the-lambda-function} + +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. +If you don't already have one, create a role with `lambda.amazonaws.com` as the trusted principal before proceeding. +This role is separate from the IAM role that Temporal uses to invoke the function. + +### i. Build and package {#build-and-package} + + + + +Cross-compile for Lambda's Linux runtime: + +```bash +GOOS=linux GOARCH=amd64 go build -tags lambda.norpc -o bootstrap ./worker +``` + +Package the binary into a zip file: + +```bash +zip function.zip bootstrap +``` + + + + +### ii. Deploy the Lambda function {#deploy-lambda-function} + +Configure the Temporal connection using Lambda environment variables. +The `lambdaworker` package reads these automatically at startup. + +| Variable | Description | Required | +|---|---|---| +| `TEMPORAL_ADDRESS` | Temporal frontend address (e.g., `..tmprl.cloud:7233`). | Yes | +| `TEMPORAL_NAMESPACE` | Temporal Namespace. | Yes | +| `HOME` | Must be set to `/tmp` so the SDK's config loader can resolve a user config directory in the Lambda environment. | Yes | +| `TEMPORAL_TASK_QUEUE` | Task Queue name. Overrides the value set in code. | No | +| `TEMPORAL_TLS_CLIENT_CERT_PATH` | Path to the TLS client certificate file for mTLS authentication. | For mTLS | +| `TEMPORAL_TLS_CLIENT_KEY_PATH` | Path to the TLS client key file for mTLS authentication. | For mTLS | +| `TEMPORAL_API_KEY` | API key for API key authentication. | For API key auth | + +For the full list of supported environment variables, see [Client environment configuration](/references/client-environment-configuration). + +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. + +```bash +aws lambda create-function \ + --function-name my-temporal-worker \ + --runtime provided.al2023 \ + --handler bootstrap \ + --architectures x86_64 \ + --role arn:aws:iam:::role/my-temporal-worker-execution \ + --zip-file fileb://function.zip \ + --timeout 60 \ + --memory-size 256 \ + --environment "Variables={HOME=/tmp,TEMPORAL_ADDRESS=:7233,TEMPORAL_NAMESPACE=,TEMPORAL_API_KEY=}" +``` + +To update an existing function with new code: + +```bash +aws lambda update-function-code \ + --function-name my-temporal-worker \ + --zip-file fileb://function.zip +``` + +## 3. Configure IAM for Temporal invocation {#configure-iam} + +Temporal needs permission to invoke your Lambda function. +The Temporal server assumes an IAM role in your AWS account to call `lambda:InvokeFunction`. +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. + +Deploy the following CloudFormation template to create the invocation role and its permissions. [Download the template](/files/temporal-cloud-serverless-worker-role.yaml). + +:::note + +This template is scoped to Temporal Cloud. Self-hosted configuration guidance is in progress. + +::: + +| Parameter | Description | +|---|---| +| `AssumeRoleExternalId` | A unique identifier that Temporal Cloud presents when assuming the role. Provided in your Namespace configuration. | +| `LambdaFunctionARNs` | Comma-separated list of Lambda function ARNs that Temporal may invoke. One role can authorize multiple Worker Lambdas. | +| `RoleName` | Base name for the created IAM role. Defaults to `Temporal-Cloud-Serverless-Worker`. | + +
+CloudFormation template + +```yaml +# CloudFormation template for creating an IAM role that Temporal Cloud can assume to invoke Lambda functions. +AWSTemplateFormatVersion: '2010-09-09' +Description: Creates an IAM role that Temporal Cloud can assume to invoke multiple Lambda functions for Serverless Workers. + +Parameters: + AssumeRoleExternalId: + Type: String + Description: The External ID provided by Temporal Cloud + AllowedPattern: '[a-zA-Z0-9_+=,.@-]*' + MinLength: 5 + MaxLength: 45 + + LambdaFunctionARNs: + Type: CommaDelimitedList + Description: >- + Comma-separated list of Lambda function ARNs to invoke + (e.g., arn:aws:lambda:us-west-2:123456789012:function:worker-1,arn:aws:lambda:us-west-2:123456789012:function:worker-2) + + RoleName: + Type: String + Default: 'Temporal-Cloud-Serverless-Worker' + +Metadata: + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: "Temporal Cloud Configuration" + Parameters: + - AssumeRoleExternalId + - Label: + default: "Lambda Configuration" + Parameters: + - LambdaFunctionARNs + - RoleName + ParameterLabels: + AssumeRoleExternalId: + default: "External ID (provided by Temporal Cloud)" + LambdaFunctionARNs: + default: "Lambda Function ARNs (comma-separated list)" + RoleName: + default: "IAM Role Name" + +Resources: + TemporalCloudServerlessWorker: + Type: AWS::IAM::Role + Properties: + RoleName: !Sub '${RoleName}-${AWS::StackName}' + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + AWS: + [ + arn:aws:iam::902542641901:role/wci-lambda-invoke, + arn:aws:iam::160190466495:role/wci-lambda-invoke, + arn:aws:iam::819232936619:role/wci-lambda-invoke, + arn:aws:iam::829909441867:role/wci-lambda-invoke, + arn:aws:iam::354116250941:role/wci-lambda-invoke + ] + Action: sts:AssumeRole + Condition: + StringEquals: + 'sts:ExternalId': [!Ref AssumeRoleExternalId] + Description: "The role Temporal Cloud uses to invoke Lambda functions for Serverless Workers" + MaxSessionDuration: 3600 # 1 hour + + TemporalCloudLambdaInvokePermissions: + Type: AWS::IAM::Policy + DependsOn: TemporalCloudServerlessWorker + Properties: + PolicyName: 'Temporal-Cloud-Lambda-Invoke-Permissions' + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - lambda:InvokeFunction + - lambda:GetFunction + Resource: !Ref LambdaFunctionARNs + Roles: + - !Sub '${RoleName}-${AWS::StackName}' + +Outputs: + RoleARN: + Description: The ARN of the IAM role created for Temporal Cloud + Value: !GetAtt TemporalCloudServerlessWorker.Arn + Export: + Name: !Sub "${AWS::StackName}-RoleARN" + + RoleName: + Description: The name of the IAM role + Value: !Ref RoleName + + LambdaFunctionARNs: + Description: The Lambda function ARNs that can be invoked + Value: !Join [", ", !Ref LambdaFunctionARNs] +``` + +
+ +Deploy the template: + +```bash +aws cloudformation create-stack \ + --stack-name \ + --template-body file://temporal-cloud-serverless-worker-role.yaml \ + --parameters \ + ParameterKey=AssumeRoleExternalId,ParameterValue= \ + ParameterKey=LambdaFunctionARNs,ParameterValue='""' \ + --capabilities CAPABILITY_NAMED_IAM \ + --region +``` + +The stack output `RoleARN` contains the IAM role ARN to use in your Worker Deployment Version's compute configuration. + +## 4. Create a Worker Deployment Version {#create-worker-deployment-version} + +Create a [Worker Deployment Version](/production-deployment/worker-deployments/worker-versioning) with a compute provider that points to your Lambda function. +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. +The deployment name and build ID must match the values in your Worker code. + +You can create the version using the Temporal UI, the Temporal CLI, or programmatically with an SDK. + +### Temporal UI + +Use the UI for one-off setup and exploration. + +When you create a version through the UI, the version is automatically set as current. + + + +### Temporal CLI + +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. + + + +```bash +temporal worker deployment create-version \ + --namespace \ + --deployment-name my-app \ + --build-id build-1 \ + --aws-lambda-invoke arn:aws:lambda:::function:my-temporal-worker +``` + +### SDK + +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. + + + + +```go +// Create the worker deployment +client.CreateWorkerDeployment(ctx, &workflowservice.CreateWorkerDeploymentRequest{ + Namespace: "default", + DeploymentName: "my-app", +}) + +// Create a version with Lambda compute config +client.CreateWorkerDeploymentVersion(ctx, &workflowservice.CreateWorkerDeploymentVersionRequest{ + Namespace: "default", + DeploymentVersion: &deploymentpb.WorkerDeploymentVersion{ + DeploymentName: "my-app", + BuildId: "build-1", + }, + ComputeConfig: &computepb.ComputeConfig{ + ScalingGroups: map[string]*computepb.ComputeConfigScalingGroup{ + "default": { + Provider: &computepb.ComputeProvider{ + Type: "aws-lambda", + Details: /* Lambda ARN, role ARN, external ID */, + }, + }, + }, + }, +}) +``` + + + + +## 5. Set the version as current {#set-current-version} + +If you created the version through the Temporal UI, the version is already current and you can skip this step. + +If you used the CLI or SDK, set the version as current. +Without this step, tasks on the Task Queue will not route to the version, and Temporal will not invoke the Lambda function. + +```bash +temporal worker deployment set-current-version \ + --deployment-name my-app \ + --build-id build-1 +``` + +## 6. Verify the deployment {#verify-the-deployment} + +Start a Workflow on the same Task Queue to confirm that Temporal invokes your Lambda Worker. + +```bash +temporal workflow start \ + --task-queue my-task-queue \ + --type MyWorkflow \ + --input '"Hello, serverless!"' +``` + +When the task lands on the Task Queue with no active pollers, Temporal detects the compute provider configuration and invokes your Lambda function. +The Worker starts, connects to Temporal, picks up the task, and processes it. + +You can verify the invocation by checking: + +- **Temporal UI:** The Workflow execution should show task completions in the event history. +- **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. diff --git a/docs/production-deployment/worker-deployments/serverless-workers/index.mdx b/docs/production-deployment/worker-deployments/serverless-workers/index.mdx new file mode 100644 index 0000000000..af8e534b31 --- /dev/null +++ b/docs/production-deployment/worker-deployments/serverless-workers/index.mdx @@ -0,0 +1,29 @@ +--- +id: index +title: Serverless Workers +sidebar_label: Serverless Workers +description: Deploy Temporal Workers on serverless compute providers. Temporal invokes your Worker when Tasks arrive, with no long-lived processes to manage. +slug: /production-deployment/worker-deployments/serverless-workers +toc_max_heading_level: 4 +keywords: + - serverless + - worker + - deployment + - lambda +tags: + - Workers + - Deploy + - Serverless +--- + +Serverless Workers let you run Temporal Workers on serverless compute like AWS Lambda. +Deploy your Worker code to a serverless provider, configure a compute provider for the Worker Deployment Version, and Temporal invokes the Worker when Tasks arrive. +There are no long-lived processes to provision or scale. + +Temporal monitors Task Queues that have a compute provider configured. +When a task arrives and no Worker is polling, Temporal invokes the configured compute target. +The Worker starts, processes available tasks, and shuts down when the invocation window ends. + +## Supported providers + +- [**AWS Lambda**](/production-deployment/worker-deployments/serverless-workers/aws-lambda) - Deploy a Go SDK Worker as a Lambda function. Temporal assumes an IAM role in your AWS account to invoke the function when Tasks arrive. diff --git a/sidebars.js b/sidebars.js index 3c348f8eea..59e46a4046 100644 --- a/sidebars.js +++ b/sidebars.js @@ -1188,6 +1188,18 @@ module.exports = { 'production-deployment/worker-deployments/worker-versioning', 'production-deployment/worker-deployments/kubernetes-controller', 'production-deployment/worker-deployments/deploy-workers-to-aws-eks', + { + type: 'category', + label: 'Serverless Workers', + collapsed: true, + link: { + type: 'doc', + id: 'production-deployment/worker-deployments/serverless-workers/index', + }, + items: [ + 'production-deployment/worker-deployments/serverless-workers/aws-lambda', + ], + }, ], }, 'production-deployment/data-encryption', diff --git a/static/files/temporal-cloud-serverless-worker-role.yaml b/static/files/temporal-cloud-serverless-worker-role.yaml new file mode 100644 index 0000000000..4b5546a5fb --- /dev/null +++ b/static/files/temporal-cloud-serverless-worker-role.yaml @@ -0,0 +1,97 @@ +# CloudFormation template for creating an IAM role that Temporal Cloud can assume to invoke Lambda functions. +AWSTemplateFormatVersion: '2010-09-09' +Description: Creates an IAM role that Temporal Cloud can assume to invoke multiple Lambda functions for Serverless Workers. + +Parameters: + AssumeRoleExternalId: + Type: String + Description: The External ID provided by Temporal Cloud + AllowedPattern: '[a-zA-Z0-9_+=,.@-]*' + MinLength: 5 + MaxLength: 45 + + LambdaFunctionARNs: + Type: CommaDelimitedList + Description: >- + Comma-separated list of Lambda function ARNs to invoke + (e.g., arn:aws:lambda:us-west-2:123456789012:function:worker-1,arn:aws:lambda:us-west-2:123456789012:function:worker-2) + + RoleName: + Type: String + Default: 'Temporal-Cloud-Serverless-Worker' + +Metadata: + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: "Temporal Cloud Configuration" + Parameters: + - AssumeRoleExternalId + - Label: + default: "Lambda Configuration" + Parameters: + - LambdaFunctionARNs + - RoleName + ParameterLabels: + AssumeRoleExternalId: + default: "External ID (provided by Temporal Cloud)" + LambdaFunctionARNs: + default: "Lambda Function ARNs (comma-separated list)" + RoleName: + default: "IAM Role Name" + +Resources: + TemporalCloudServerlessWorker: + Type: AWS::IAM::Role + Properties: + RoleName: !Sub '${RoleName}-${AWS::StackName}' + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + AWS: + [ + arn:aws:iam::902542641901:role/wci-lambda-invoke, + arn:aws:iam::160190466495:role/wci-lambda-invoke, + arn:aws:iam::819232936619:role/wci-lambda-invoke, + arn:aws:iam::829909441867:role/wci-lambda-invoke, + arn:aws:iam::354116250941:role/wci-lambda-invoke + ] + Action: sts:AssumeRole + Condition: + StringEquals: + 'sts:ExternalId': [!Ref AssumeRoleExternalId] + Description: "The role Temporal Cloud uses to invoke Lambda functions for Serverless Workers" + MaxSessionDuration: 3600 # 1 hour + + TemporalCloudLambdaInvokePermissions: + Type: AWS::IAM::Policy + DependsOn: TemporalCloudServerlessWorker + Properties: + PolicyName: 'Temporal-Cloud-Lambda-Invoke-Permissions' + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - lambda:InvokeFunction + - lambda:GetFunction + Resource: !Ref LambdaFunctionARNs + Roles: + - !Sub '${RoleName}-${AWS::StackName}' + +Outputs: + RoleARN: + Description: The ARN of the IAM role created for Temporal Cloud + Value: !GetAtt TemporalCloudServerlessWorker.Arn + Export: + Name: !Sub "${AWS::StackName}-RoleARN" + + RoleName: + Description: The name of the IAM role + Value: !Ref RoleName + + LambdaFunctionARNs: + Description: The Lambda function ARNs that can be invoked + Value: !Join [", ", !Ref LambdaFunctionARNs] From 18f8d0e8f61c880f1623ce58dc2efba0c58aa4bc Mon Sep 17 00:00:00 2001 From: Lenny Chen <55669665+lennessyy@users.noreply.github.com> Date: Thu, 16 Apr 2026 16:38:04 -0700 Subject: [PATCH 03/27] docs: Serverless Workers - Evaluate pages (1/4) (#4417) * docs: Serverless Workers - Evaluate pages (1/4) Add the Evaluate section for Serverless Workers documentation: - Serverless Workers overview page - Interactive demo page with ServerlessWorkerDemo component - Sidebar entry under Features - Redirect from old demo URL - Change onBrokenLinks/onBrokenAnchors to 'warn' for incremental PRs Part 1 of 4, splitting PR #4405 into smaller PRs. Co-Authored-By: Claude Opus 4.6 (1M context) * docs: address PM feedback on evaluate page - Fix polling description: serverless workers still poll, the difference is lifecycle - Tone down operational overhead claims: customers still deploy and configure - Clarify long-running limitation applies to activities, not workflows Co-Authored-By: Claude Opus 4.6 (1M context) * docs: address review feedback on Evaluate pages - Reframe lifecycle description: Temporal invokes the Serverless Worker on demand (bchav, akhayam) - Clarify operational overhead: offload invocation and scaling, but deployments remain the user's responsibility (bchav) - Introduce "long-lived Workers" terminology and use consistently - Sharpen Lambda execution limit wording and Cloud Run callout (akhayam, bchav) - Remove Worker Versioning row from comparison table as too low-level (akhayam) - Remove --scaler-min-instances, --scaler-max-instances, and --ignore-missing-task-queues from demo CLI snippets (smuneebahmad) - Remove Min/Max Instances config fields from demo UI (smuneebahmad) Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- .../serverless-workers/demo.mdx | 41 ++ .../serverless-workers/index.mdx | 97 ++++ sidebars.js | 8 + .../elements/ServerlessWorkerDemo.js | 533 ++++++++++++++++++ .../serverless-worker-demo.module.css | 413 ++++++++++++++ src/components/index.js | 1 + vercel.json | 5 + 7 files changed, 1098 insertions(+) create mode 100644 docs/evaluate/development-production-features/serverless-workers/demo.mdx create mode 100644 docs/evaluate/development-production-features/serverless-workers/index.mdx create mode 100644 src/components/elements/ServerlessWorkerDemo.js create mode 100644 src/components/elements/serverless-worker-demo.module.css diff --git a/docs/evaluate/development-production-features/serverless-workers/demo.mdx b/docs/evaluate/development-production-features/serverless-workers/demo.mdx new file mode 100644 index 0000000000..fcfad1a6b6 --- /dev/null +++ b/docs/evaluate/development-production-features/serverless-workers/demo.mdx @@ -0,0 +1,41 @@ +--- +id: demo +title: Serverless Workers Interactive Demo +sidebar_label: Interactive Demo +slug: /evaluate/serverless-workers/demo +toc_max_heading_level: 3 +keywords: + - serverless + - lambda + - aws + - worker + - interactive demo + - serverless worker +tags: + - Workers + - Serverless + - AWS Lambda +description: An interactive demo for Temporal Serverless Workers. Explore the configuration, generated code, and execution flow for running Workers on AWS Lambda. +--- + +Serverless Workers let you run Temporal Workers on serverless compute like AWS Lambda. +There are no long-lived processes to provision or scale. +Temporal Cloud invokes your Worker when Tasks arrive, and the Worker shuts down when the work is done. + +Use the interactive demo below to explore how the configuration options affect the generated +Worker code, deployment script, and CLI commands. Click "Start Workflow" to simulate the +end-to-end Serverless Worker invocation flow. + + + +import { ServerlessWorkerDemo } from '@site/src/components'; + + + + +--- + +## Next steps + +- [Serverless Workers - Go SDK](/develop/go/workers/serverless-workers/aws-lambda) for SDK-specific configuration, defaults, and lifecycle details. +- [Deploy a Serverless Worker](/production-deployment/worker-deployments/serverless-workers) for the full end-to-end deployment guide. diff --git a/docs/evaluate/development-production-features/serverless-workers/index.mdx b/docs/evaluate/development-production-features/serverless-workers/index.mdx new file mode 100644 index 0000000000..833f04d954 --- /dev/null +++ b/docs/evaluate/development-production-features/serverless-workers/index.mdx @@ -0,0 +1,97 @@ +--- +id: index +title: Serverless Workers +sidebar_label: Serverless Workers +slug: /evaluate/serverless-workers +description: Understand the benefits of Serverless Workers and when to use them. Run Temporal Workers on serverless compute with no infrastructure to manage. +toc_max_heading_level: 4 +keywords: + - serverless + - lambda + - aws + - worker + - serverless worker + - evaluate +tags: + - Workers + - Serverless +--- + +Serverless Workers let you run Temporal Workers on serverless compute platforms like AWS Lambda. +There are no servers to provision, no clusters to scale, and no idle compute to pay for. +Temporal invokes the Worker when Tasks arrive, and the Worker shuts down when the work is done. + +Serverless Workers use the same Temporal SDKs as traditional long-lived Workers. +You register Workflows and Activities the same way. +The difference is in the lifecycle: instead of running a long-lived process, Temporal invokes the Serverless Worker on demand when Tasks arrive. The Worker starts, polls for available Tasks, processes them, and exits when the work is done. + +For a deeper look at how Serverless invocation works under the hood, see [Serverless Workers](/serverless-workers) in the encyclopedia. + +## Benefits + +### Reduce operational overhead + +Long-lived Workers require you to provision infrastructure, configure scaling policies, manage deployments, and monitor host-level health. +Serverless Workers reduce this burden by offloading invocation and scaling to Temporal and the compute provider. +You still deploy the function and configure the compute provider, but there is no always-on infrastructure to manage and no autoscaling policies to tune. + +Worker management is one of the most common sources of support questions for Temporal users. +Serverless Workers offer a prescriptive deployment path that reduces the operational surface area and lets you focus on writing Workflows instead of managing infrastructure. + +### Get started faster + +Running a long-lived Worker requires choosing a hosting strategy, configuring compute resources, and setting up deployment pipelines before you can execute your first Workflow. + +With Serverless Workers, deploying a Worker is as simple as deploying a function. +Package your Worker code, deploy it to your serverless provider, and configure the connection to Temporal. There is no need to set up Kubernetes, manage container orchestration, or design a scaling strategy. + +### Scale automatically + +Serverless compute providers handle scaling natively. +When Task volume increases, the provider spins up additional function instances. +When traffic drops, instances scale down. When there is no work, there is no compute running. + +This automatic scaling is especially useful for bursty, event-driven workloads where traffic patterns are unpredictable or highly variable. + +### Pay only for what you use + +Long-lived Workers run continuously, whether or not there is work to process. +Serverless Workers run only when Tasks are available. +For workloads with low or intermittent volume, this pay-per-invocation model can significantly reduce compute costs. + +## When to use Serverless Workers + +Serverless Workers are a good fit when: + +- **Workloads are bursty or event-driven.** Order processing, notifications, webhook handlers, and similar workloads that experience spiky traffic benefit from automatic scaling without over-provisioning. +- **Traffic is low or intermittent.** If Workers spend most of their time idle, Serverless Workers eliminate the cost of always-on compute. +- **You want a simpler getting-started path.** Deploying a function is simpler than setting up a container orchestration platform. Serverless Workers reduce the steps between writing Worker code and running your first Workflow. +- **Your organization has standardized on serverless.** Teams that already run services on Lambda, Cloud Run, or similar platforms can run Temporal Workers using the same deployment patterns and tooling. +- **You serve multiple tenants with infrequent workloads.** Platforms that run Workflows on behalf of many users or customers can avoid running dedicated Workers per tenant. + +Serverless Workers may not be ideal when: + +- **Activities are long-running and cannot be interrupted.** Some serverless platforms enforce execution time limits. For example, AWS Lambda has a 15-minute execution limit. Activities that run longer than the provider's timeout and cannot be broken into smaller steps need a different hosting strategy or a provider with longer limits (such as Cloud Run). Long-running Workflows are not affected because Workflows can span multiple invocations. +- **Workloads require sustained high throughput.** For consistently high-volume Task Queues, long-lived Workers on dedicated compute may be more cost-effective and performant. +- **You need persistent connections.** Some features require a persistent connection between the Worker and Temporal, which serverless invocations do not maintain. + +## How Serverless Workers compare to long-lived Workers + +| | Long-lived Worker | Serverless Worker | +|---|---|---| +| **Lifecycle** | Long-lived process that runs continuously. | Invoked on demand. Starts and stops per invocation. | +| **Scaling** | You manage scaling (Kubernetes HPA, instance count, etc.). | Temporal invokes additional instances as needed, within the compute provider's concurrency limits. | +| **Connection** | Persistent connection to Temporal. | Fresh connection on each invocation. | + +## Supported providers + +| Provider | Status | +|---|---| +| AWS Lambda | Available | + +## Next steps + +- [Interactive demo](/evaluate/serverless-workers/demo) to explore the configuration and invocation flow. +- [How Serverless Workers work](/serverless-workers) for a deeper look at the invocation lifecycle, compute providers, and architecture. +- [Deploy a Serverless Worker](/production-deployment/worker-deployments/serverless-workers) for the end-to-end deployment guide. +- [Serverless Workers - Go SDK](/develop/go/workers/serverless-workers/aws-lambda) for SDK-specific configuration and defaults. diff --git a/sidebars.js b/sidebars.js index 59e46a4046..96c06b2df9 100644 --- a/sidebars.js +++ b/sidebars.js @@ -37,6 +37,14 @@ module.exports = { 'evaluate/development-production-features/low-latency', 'evaluate/development-production-features/multi-tenancy', 'evaluate/development-production-features/job-queue', + { + type: 'category', + label: 'Serverless Workers', + link: { type: 'doc', id: 'evaluate/development-production-features/serverless-workers/index' }, + items: [ + 'evaluate/development-production-features/serverless-workers/demo', + ], + }, { type: 'category', label: 'Product release stages', diff --git a/src/components/elements/ServerlessWorkerDemo.js b/src/components/elements/ServerlessWorkerDemo.js new file mode 100644 index 0000000000..66db759d67 --- /dev/null +++ b/src/components/elements/ServerlessWorkerDemo.js @@ -0,0 +1,533 @@ +import Admonition from '@theme/Admonition'; +import CodeBlock from '@theme/CodeBlock'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import styles from './serverless-worker-demo.module.css'; + +// --------------------------------------------------------------------------- +// Code generation +// --------------------------------------------------------------------------- + +function generateWorkerCode(config) { + const { deploymentName, buildId, taskQueue } = config; + return `package main + +import ( +\tlambdaworker "go.temporal.io/sdk/contrib/aws/lambdaworker" +\t"go.temporal.io/sdk/worker" +\t"go.temporal.io/sdk/workflow" +) + +func main() { +\tlambdaworker.RunWorker(worker.WorkerDeploymentVersion{ +\t\tDeploymentName: "${deploymentName}", +\t\tBuildID: "${buildId}", +\t}, func(opts *lambdaworker.Options) error { +\t\topts.TaskQueue = "${taskQueue}" + +\t\topts.RegisterWorkflowWithOptions(MyWorkflow, workflow.RegisterOptions{ +\t\t\tVersioningBehavior: workflow.VersioningBehaviorAutoUpgrade, +\t\t}) +\t\topts.RegisterActivity(MyActivity) + +\t\treturn nil +\t}) +}`; +} + +function generateDeployScript(config) { + const { namespace, lambdaFunctionName } = config; + return `# Build for Lambda +GOOS=linux GOARCH=amd64 go build -tags lambda.norpc -o bootstrap ./worker + +# Package the binary +zip function.zip bootstrap + +# Create the Lambda function with Temporal connection env vars +aws lambda create-function \\ + --function-name ${lambdaFunctionName} \\ + --runtime provided.al2023 \\ + --handler bootstrap \\ + --architectures x86_64 \\ + --role arn:aws:iam:::role/my-temporal-worker-execution \\ + --zip-file fileb://function.zip \\ + --timeout 60 \\ + --memory-size 256 \\ + --environment "Variables={TEMPORAL_ADDRESS=${namespace}.tmprl.cloud:7233,TEMPORAL_NAMESPACE=${namespace}}"`; +} + +function generateIamScript(config) { + const { lambdaArn } = config; + return `# Deploy the CloudFormation template to create the invocation role +aws cloudformation create-stack \\ + --stack-name my-temporal-invoke-role \\ + --template-body file://temporal-invoke-role.yaml \\ + --parameters \\ + ParameterKey=TemporalPrincipalArn,ParameterValue= \\ + ParameterKey=ExternalId,ParameterValue= \\ + ParameterKey=LambdaFunctionArn,ParameterValue=${lambdaArn} \\ + --capabilities CAPABILITY_IAM + +# TemporalPrincipalArn and ExternalId are provided by Temporal +# in your Namespace configuration.`; +} + +function generateCliCode(config) { + const { namespace, deploymentName, buildId, lambdaArn } = config; + return `temporal worker deployment create-version \\ + --namespace ${namespace} \\ + --deployment-name ${deploymentName} \\ + --build-id ${buildId} \\ + --aws-lambda-invoke ${lambdaArn}`; +} + +function generateSetCurrentVersion(config) { + const { namespace, deploymentName, buildId } = config; + return `temporal worker deployment set-current-version \\ + --namespace ${namespace} \\ + --deployment-name ${deploymentName} \\ + --build-id ${buildId}`; +} + +function generateStartWorkflow(config) { + const { namespace, taskQueue } = config; + return `temporal workflow start \\ + --namespace ${namespace} \\ + --task-queue ${taskQueue} \\ + --type MyWorkflow \\ + --input '"Hello, serverless!"'`; +} + +// --------------------------------------------------------------------------- +// Steps configuration — ties steps to code tabs +// --------------------------------------------------------------------------- + +const STEPS = [ + { + id: 'worker', + number: '1', + title: 'Write worker code', + description: + 'Use the lambdaworker package to write a Worker that runs inside a Lambda function. Register Workflows and Activities the same way you would with a standard Worker.', + codeLabel: 'Worker Code', + language: 'go', + generate: generateWorkerCode, + }, + { + id: 'deploy', + number: '2', + title: 'Deploy to Lambda', + description: + 'Cross-compile for Linux, package the binary, and create the Lambda function. Configure the Temporal connection using environment variables.', + codeLabel: 'Deploy Script', + language: 'bash', + generate: generateDeployScript, + }, + { + id: 'iam', + number: '3', + title: 'Configure IAM for Temporal invocation', + description: + 'Deploy a CloudFormation template that creates an IAM role allowing Temporal to invoke your Lambda function. The principal ARN and External ID are provided in your Temporal Namespace configuration.', + codeLabel: 'IAM Setup', + language: 'bash', + generate: generateIamScript, + }, + { + id: 'cli', + number: '4', + title: 'Create a Worker Deployment Version', + description: + 'Use the CLI to create a Worker Deployment Version with your Lambda ARN as the compute provider. The deployment name and build ID must match your Worker code.', + codeLabel: 'CLI Command', + language: 'bash', + generate: generateCliCode, + }, + { + id: 'set-current', + number: '5', + title: 'Set the current version', + description: + 'Promote the version to current so Temporal routes Tasks to it. New Workflow Executions and auto-upgrade Workflows will use this version.', + codeLabel: 'CLI Command', + language: 'bash', + generate: generateSetCurrentVersion, + }, + { + id: 'start', + number: '6', + title: 'Start a Workflow', + description: + 'Start a Workflow on the same Task Queue. When the Task arrives with no active pollers, Temporal invokes your Lambda function. The Worker starts, processes the Task, and shuts down.', + codeLabel: 'Start Workflow', + language: 'bash', + generate: generateStartWorkflow, + }, +]; + +// --------------------------------------------------------------------------- +// Flow diagram nodes +// --------------------------------------------------------------------------- + +const FLOW_NODES = [ + { label: 'Start', sub: 'Client' }, + { label: 'Task Queue', sub: 'No pollers' }, + { label: 'Temporal', sub: 'Invokes Lambda' }, + { label: 'Worker', sub: 'Lambda' }, + { label: 'Done', sub: 'Result' }, +]; + +const IDLE_NODES = Array(FLOW_NODES.length).fill('pending'); + +const DEFAULT_CONFIG = { + deploymentName: 'my-app', + buildId: 'build-1', + taskQueue: 'serverless-task-queue', + namespace: 'your-namespace.your-account', + lambdaFunctionName: 'my-temporal-worker', + lambdaArn: 'arn:aws:lambda:us-east-1:123456789012:function:my-temporal-worker', +}; + +// --------------------------------------------------------------------------- +// Main component +// --------------------------------------------------------------------------- + +export default function ServerlessWorkerDemo() { + const [activeStep, setActiveStep] = useState(0); + const [config, setConfig] = useState({ ...DEFAULT_CONFIG }); + + const [sim, setSim] = useState({ + running: false, + nodeStates: [...IDLE_NODES], + log: [], + status: 'idle', + result: null, + }); + + const runIdRef = useRef(0); + const logScrollRef = useRef(null); + const codeRef = useRef(null); + + useEffect(() => { + if (logScrollRef.current) { + logScrollRef.current.scrollTop = logScrollRef.current.scrollHeight; + } + }, [sim.log]); + + const updateConfig = useCallback((key, value) => { + setConfig((prev) => ({ ...prev, [key]: value })); + }, []); + + const handleFunctionNameChange = useCallback((value) => { + setConfig((prev) => ({ + ...prev, + lambdaFunctionName: value, + lambdaArn: `arn:aws:lambda:us-east-1:123456789012:function:${value}`, + })); + }, []); + + const handleStepClick = useCallback((index) => { + setActiveStep(index); + }, []); + + const currentStep = STEPS[activeStep]; + const code = currentStep.generate(config); + + const handleSimulate = useCallback(() => { + const runId = ++runIdRef.current; + const isCancelled = () => runIdRef.current !== runId; + const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + + const startTime = Date.now(); + const logEntries = []; + + const elapsed = () => ((Date.now() - startTime) / 1000).toFixed(2); + + const update = (nodeStates, msg, type = 'info') => { + if (isCancelled()) return; + if (msg) logEntries.push({ time: elapsed(), msg, type }); + setSim((prev) => ({ + ...prev, + running: true, + nodeStates, + log: [...logEntries], + status: 'running', + })); + }; + + // Jump to the last step and start simulation + setActiveStep(STEPS.length - 1); + + setSim({ + running: true, + nodeStates: [...IDLE_NODES], + log: [], + status: 'running', + result: null, + }); + + (async () => { + update( + ['active', 'pending', 'pending', 'pending', 'pending'], + `Workflow started on task queue "${config.taskQueue}"...` + ); + await sleep(600); + if (isCancelled()) return; + + update( + ['completed', 'active', 'pending', 'pending', 'pending'], + 'Task enqueued. No active pollers detected on task queue.' + ); + await sleep(800); + if (isCancelled()) return; + + update( + ['completed', 'completed', 'active', 'pending', 'pending'], + 'Temporal Service detected unpolled task. Invoking compute provider...' + ); + await sleep(600); + if (isCancelled()) return; + + update( + ['completed', 'completed', 'active', 'pending', 'pending'], + 'Assuming IAM role via cross-account trust. Validating External ID.', + 'info' + ); + await sleep(500); + if (isCancelled()) return; + + update( + ['completed', 'completed', 'completed', 'active', 'pending'], + `Lambda function "${config.lambdaFunctionName}" invoked.` + ); + await sleep(500); + if (isCancelled()) return; + + update( + ['completed', 'completed', 'completed', 'active', 'pending'], + `Worker started. Deployment: "${config.deploymentName}", Build: "${config.buildId}".` + ); + await sleep(400); + if (isCancelled()) return; + + update( + ['completed', 'completed', 'completed', 'active', 'pending'], + `Worker polling task queue "${config.taskQueue}" for tasks...` + ); + await sleep(500); + if (isCancelled()) return; + + update( + ['completed', 'completed', 'completed', 'active', 'pending'], + 'Picked up Workflow Task. Executing MyWorkflow...' + ); + await sleep(600); + if (isCancelled()) return; + + update( + ['completed', 'completed', 'completed', 'active', 'pending'], + 'Executing Activity: MyActivity...' + ); + await sleep(500); + if (isCancelled()) return; + + const result = 'Workflow completed successfully'; + logEntries.push({ + time: elapsed(), + msg: 'Activity completed. Workflow result returned to caller.', + type: 'success', + }); + logEntries.push({ + time: elapsed(), + msg: 'Worker draining. Graceful shutdown before Lambda deadline.', + type: 'info', + }); + + setSim({ + running: false, + nodeStates: ['completed', 'completed', 'completed', 'completed', 'completed'], + log: [...logEntries], + status: 'completed', + result, + }); + })(); + }, [config]); + + return ( +
+ {/* ── Simulation: the main interactive area ── */} +
+
+
+

Serverless Worker Flow

+
+ {FLOW_NODES.map((node, i) => ( + +
+
{node.label}
+
{node.sub}
+
+ {i < FLOW_NODES.length - 1 && ( +
+ › +
+ )} +
+ ))} +
+
+ +
+

Execution Log

+
+ {sim.log.length === 0 ? ( +
+ Click “Simulate Workflow” to see the serverless worker flow in action +
+ ) : ( + sim.log.map((entry, i) => ( +
+ [{entry.time}s] + {entry.msg} +
+ )) + )} +
+ + {sim.status === 'completed' && ( +
Workflow completed successfully.
+ )} +
+
+ +
+
+ +
+ +
+

Configuration

+
+ updateConfig('deploymentName', v)} + /> + updateConfig('buildId', v)} + /> + updateConfig('taskQueue', v)} + /> + updateConfig('namespace', v)} + /> + +
+
+
+
+ + + The execution log above is a simplified, combined view for educational purposes. + In a real Serverless Worker execution, logs are distributed across different services + (Temporal, AWS Lambda, your application) with varying visibility. + + + {/* ── Step-by-step walkthrough: steps left, code right ── */} +
+
+

Step-by-step walkthrough

+
+ {STEPS.map((step, i) => ( + + ))} +
+
+ +
+

+ Step {currentStep.number}:{' '} + {currentStep.codeLabel} +

+ {code} +
+
+
+ ); +} + +// --------------------------------------------------------------------------- +// Sub-components +// --------------------------------------------------------------------------- + +function ConfigField({ label, value, onChange, type = 'text', min, max }) { + return ( +
+ + onChange(e.target.value)} + /> +
+ ); +} diff --git a/src/components/elements/serverless-worker-demo.module.css b/src/components/elements/serverless-worker-demo.module.css new file mode 100644 index 0000000000..9922156327 --- /dev/null +++ b/src/components/elements/serverless-worker-demo.module.css @@ -0,0 +1,413 @@ +/* ── Root ───────────────────────────────────────────────────────────────── */ + +.demo { + font-family: var(--ifm-font-family-base); +} + +/* ── Steps + Code side-by-side ─────────────────────────────────────────── */ + +.stepsAndCode { + display: flex; + gap: 24px; + align-items: flex-start; + margin-top: 32px; + padding-top: 24px; + border-top: 1px solid var(--ifm-color-emphasis-200); +} + +.stepsCol { + flex: 0 0 280px; + min-width: 0; +} + +.codeCol { + flex: 1; + min-width: 0; + position: sticky; + top: 80px; +} + +@media (max-width: 900px) { + .stepsAndCode { + flex-direction: column; + } + .stepsCol { + flex: none; + width: 100%; + } +} + +/* ── Two-column layout (simulation + config) ──────────────────────────── */ + +.columns { + display: flex; + gap: 24px; + align-items: flex-start; +} + +.leftCol, +.rightCol { + flex: 1; + min-width: 0; +} + +@media (max-width: 900px) { + .columns { + flex-direction: column; + } +} + +/* ── Section ────────────────────────────────────────────────────────────── */ + +.section { + margin-bottom: 20px; +} + +.sectionTitle { + font-size: 0.95rem; + font-weight: 600; + margin: 0 0 8px; + color: var(--ifm-font-color-base); +} + +/* ── Config form ────────────────────────────────────────────────────────── */ + +.configGrid { + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 6px; + overflow: hidden; +} + +.configRow { + display: flex; + align-items: center; + padding: 7px 12px; + border-bottom: 1px solid var(--ifm-color-emphasis-200); + background: var(--ifm-background-surface-color); +} + +.configRow:last-child { + border-bottom: none; +} + +.configLabel { + flex: 1; + font-size: 0.83rem; + color: var(--ifm-font-color-secondary); + user-select: none; +} + +.configInput { + width: 200px; + padding: 4px 8px; + border: 1px solid var(--ifm-color-emphasis-300); + border-radius: 4px; + background: var(--ifm-background-color); + color: var(--ifm-font-color-base); + font-size: 0.83rem; +} + +/* ── Code tabs ──────────────────────────────────────────────────────────── */ + +.codeTabs { + display: flex; + gap: 4px; + margin-bottom: 8px; + flex-wrap: wrap; +} + +.codeTab { + padding: 4px 12px; + border: 1px solid var(--ifm-color-emphasis-300); + border-radius: 4px; + background: var(--ifm-background-color); + color: var(--ifm-font-color-base); + cursor: pointer; + font-size: 0.8rem; + font-weight: 500; + transition: background 0.15s, color 0.15s, border-color 0.15s; +} + +.codeTab:hover { + background: var(--ifm-color-emphasis-100); +} + +.codeTabActive { + background: var(--ifm-color-primary); + color: #fff; + border-color: var(--ifm-color-primary); +} + +/* ── Execute button ─────────────────────────────────────────────────────── */ + +.executeBtn { + width: 100%; + padding: 12px 24px; + background: var(--ifm-color-primary); + color: #fff; + border: none; + border-radius: 6px; + font-size: 1rem; + font-weight: 600; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + transition: background 0.2s; +} + +.executeBtn:hover:not(.executeBtnDisabled) { + background: var(--ifm-color-primary-dark); +} + +.executeBtnDisabled { + opacity: 0.65; + cursor: not-allowed; +} + +/* ── Spinner ────────────────────────────────────────────────────────────── */ + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.spinner { + display: inline-block; + width: 14px; + height: 14px; + border: 2px solid rgba(255, 255, 255, 0.35); + border-top-color: #fff; + border-radius: 50%; + animation: spin 0.75s linear infinite; + flex-shrink: 0; +} + +/* ── Flow diagram ───────────────────────────────────────────────────────── */ + +.flowDiagram { + display: flex; + align-items: center; + justify-content: space-between; + gap: 2px; + padding: 12px 4px; + background: var(--ifm-background-surface-color); + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 6px; + overflow-x: auto; +} + +.flowNode { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-width: 44px; + flex: 1; + padding: 6px 3px; + border-radius: 6px; + border: 2px solid var(--ifm-color-emphasis-300); + background: var(--ifm-background-color); + transition: border-color 0.3s, background 0.3s, box-shadow 0.3s; +} + +.flowNodeLabel { + font-size: 0.68rem; + font-weight: 600; + text-align: center; + line-height: 1.2; +} + +.flowNodeSub { + font-size: 0.58rem; + color: var(--ifm-font-color-secondary); + text-align: center; + margin-top: 2px; +} + +/* Node states */ +.flowNode_pending { + border-color: var(--ifm-color-emphasis-300); + opacity: 0.5; +} + +.flowNode_active { + border-color: var(--ifm-color-primary); + background: var(--ifm-color-primary-lightest, rgba(55, 125, 255, 0.08)); + box-shadow: 0 0 8px rgba(55, 125, 255, 0.25); + opacity: 1; +} + +.flowNode_completed { + border-color: var(--ifm-color-success); + opacity: 1; +} + +.flowNode_failed { + border-color: var(--ifm-color-danger); + opacity: 1; +} + +.flowArrow { + font-size: 1.1rem; + color: var(--ifm-color-emphasis-300); + flex-shrink: 0; + transition: color 0.3s; + user-select: none; +} + +.flowArrowLit { + color: var(--ifm-color-success); +} + +/* ── Log ────────────────────────────────────────────────────────────────── */ + +.log { + background: var(--ifm-pre-background); + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 6px; + padding: 12px; + max-height: 260px; + overflow-y: auto; + font-family: var(--ifm-font-family-monospace); + font-size: 0.78rem; + line-height: 1.6; +} + +.logPlaceholder { + color: var(--ifm-font-color-secondary); + font-style: italic; + text-align: center; + padding: 24px 0; +} + +.logLine { + display: flex; + gap: 8px; +} + +.logTime { + color: var(--ifm-font-color-secondary); + flex-shrink: 0; + min-width: 52px; +} + +.logMsg { + word-break: break-word; +} + +.logLine_info .logMsg { + color: var(--ifm-font-color-base); +} + +.logLine_success .logMsg { + color: var(--ifm-color-success-darkest, #2e7d32); +} + +.logLine_error .logMsg { + color: var(--ifm-color-danger); +} + +.logLine_warn .logMsg { + color: var(--ifm-color-warning-darkest, #e65100); +} + +/* ── Result banner ──────────────────────────────────────────────────────── */ + +.resultSuccess { + margin-top: 10px; + padding: 10px 14px; + background: var(--ifm-color-success-contrast-background, rgba(46, 125, 50, 0.08)); + border: 1px solid var(--ifm-color-success); + border-radius: 6px; + font-size: 0.85rem; + font-weight: 600; + color: var(--ifm-color-success-darkest, #2e7d32); +} + +/* ── Steps (clickable) ──────────────────────────────────────────────────── */ + +.stepsContainer { + display: flex; + flex-direction: column; + gap: 4px; +} + +.step { + display: flex; + gap: 10px; + align-items: flex-start; + padding: 8px 12px; + border: 1px solid var(--ifm-color-emphasis-200); + border-radius: 6px; + background: var(--ifm-background-surface-color); + cursor: pointer; + transition: border-color 0.2s, background 0.2s, box-shadow 0.2s; + text-align: left; + width: 100%; + font-family: inherit; + color: inherit; +} + +.step:hover { + border-color: var(--ifm-color-emphasis-400); + background: var(--ifm-color-emphasis-100); +} + +.stepActive { + border-color: var(--ifm-color-primary); + background: var(--ifm-color-primary-lightest, rgba(55, 125, 255, 0.06)); + box-shadow: 0 0 0 1px var(--ifm-color-primary); +} + +.stepActive:hover { + border-color: var(--ifm-color-primary); + background: var(--ifm-color-primary-lightest, rgba(55, 125, 255, 0.06)); +} + +.stepNumber { + flex-shrink: 0; + width: 24px; + height: 24px; + border-radius: 50%; + background: var(--ifm-color-emphasis-300); + color: #fff; + font-size: 0.75rem; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + margin-top: 1px; + transition: background 0.2s; +} + +.stepNumberActive { + background: var(--ifm-color-primary); +} + +.stepContent { + flex: 1; + min-width: 0; +} + +.stepTitle { + font-size: 0.83rem; + font-weight: 600; + line-height: 1.3; +} + +.stepDesc { + font-size: 0.78rem; + color: var(--ifm-font-color-secondary); + line-height: 1.4; + margin-top: 4px; +} + +/* ── Code step label ────────────────────────────────────────────────────── */ + +.codeStepLabel { + color: var(--ifm-color-primary); + font-weight: 700; +} diff --git a/src/components/index.js b/src/components/index.js index 07b4ad9ef6..9c82476efc 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -5,6 +5,7 @@ export { SdkLogos } from './elements/SdkLogos'; export { SdkLogosAsBlocks } from './elements/SdkLogosAsBlocks'; export { default as PhotoCarousel } from './elements/PhotoCarousel'; export { default as SdkTabs } from './elements/SdkTabs'; +export { default as ServerlessWorkerDemo } from './elements/ServerlessWorkerDemo'; // Formatting components export { default as DocsTable, NewDocsCell, DocsTableRow } from './formatting/DocsTable'; diff --git a/vercel.json b/vercel.json index e619ec5eb1..b8abfc2585 100644 --- a/vercel.json +++ b/vercel.json @@ -5,6 +5,11 @@ "silent": true }, "redirects": [ + { + "source": "/evaluate/serverless-workers-demo", + "destination": "/evaluate/serverless-workers/demo", + "permanent": true + }, { "source": "/cloud/introduction", "destination": "/cloud/overview", From 60b24b9721d66a9770613ed904c9b25cadfeee7e Mon Sep 17 00:00:00 2001 From: Lenny Chen <55669665+lennessyy@users.noreply.github.com> Date: Fri, 17 Apr 2026 13:31:24 -0700 Subject: [PATCH 04/27] docs: Serverless Workers - Encyclopedia (4/4) (#4415) * docs: add Serverless Workers encyclopedia page and update related pages Add the encyclopedia entry for Serverless Workers, update workers.mdx and task-queues.mdx with serverless references, add the architecture diagram, update sidebar, and add "Serverless Worker" to Vale terms. Change onBrokenLinks/onBrokenAnchors to 'warn' to accommodate cross-references to pages in other PRs in this series. Co-Authored-By: Claude Opus 4.6 (1M context) * Remove differences section from encyclopedia page The comparison table lives on the evaluate page now. Co-Authored-By: Claude Opus 4.6 (1M context) * Fix Worker invocation wording in encyclopedia Co-Authored-By: Claude Opus 4.6 (1M context) * docs: address review feedback on encyclopedia page - Use "traditional long-lived Worker" on first contrast, "long-lived Worker" thereafter (akhayam, smuneebahmad) - Replace "triggers the compute environment" with "invokes the Serverless Worker on demand" (akhayam) - Use "shuts down" instead of "exits" to match AWS Lambda runtime terminology (akhayam) - Fix "serverless function" -> "Serverless Worker" for cross-provider accuracy - Use "invocation deadline" for consistency with other pages - Add Worker lifecycle section with new lifecycle diagram (addresses akhayam's suggestion to mirror AWS's Lambda lifecycle diagram) - Explain Worker stop timeout and shutdown deadline buffer, including tuning guidance for long-running Activities and the consequences of raising one knob without the other Co-Authored-By: Claude Opus 4.7 (1M context) * docs: crop lifecycle diagram Co-Authored-By: Claude Opus 4.7 (1M context) * docs: add light/dark themed diagrams with figure captions Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- .../workers/serverless-workers.mdx | 140 ++++++++++++++++++ docs/encyclopedia/workers/task-queues.mdx | 2 +- docs/encyclopedia/workers/workers.mdx | 2 +- sidebars.js | 1 + .../diagrams/serverless-worker-flow-dark.svg | 1 + static/diagrams/serverless-worker-flow.svg | 1 + .../serverless-worker-lifecycle-dark.svg | 1 + .../diagrams/serverless-worker-lifecycle.svg | 1 + vale/styles/Temporal/terms.yml | 2 + 9 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 docs/encyclopedia/workers/serverless-workers.mdx create mode 100644 static/diagrams/serverless-worker-flow-dark.svg create mode 100644 static/diagrams/serverless-worker-flow.svg create mode 100644 static/diagrams/serverless-worker-lifecycle-dark.svg create mode 100644 static/diagrams/serverless-worker-lifecycle.svg diff --git a/docs/encyclopedia/workers/serverless-workers.mdx b/docs/encyclopedia/workers/serverless-workers.mdx new file mode 100644 index 0000000000..d0c7a24933 --- /dev/null +++ b/docs/encyclopedia/workers/serverless-workers.mdx @@ -0,0 +1,140 @@ +--- +id: serverless-workers +title: Serverless Workers +sidebar_label: Serverless Workers +description: + Learn how Serverless Workers work, how Temporal invokes them, and how they differ from traditional long-lived Workers. +slug: /serverless-workers +toc_max_heading_level: 4 +keywords: + - serverless + - workers + - lambda + - compute provider +tags: + - Workers + - Concepts + - Serverless +--- + +import ThemedImage from '@theme/ThemedImage'; + +This page covers the following: + +- [What is a Serverless Worker?](#serverless-worker) +- [How Serverless invocation works](#how-invocation-works) +- [Worker lifecycle](#worker-lifecycle) +- [Compute providers](#compute-providers) + +## What is a Serverless Worker? {#serverless-worker} + +A Serverless Worker is a Temporal Worker that runs on serverless compute instead of a long-lived process. There is no +always-on infrastructure to provision or scale. Temporal invokes the Worker when Tasks arrive on a Task Queue, and the +Worker shuts down when the work is done. + +A Serverless Worker uses the same Temporal SDKs as a traditional long-lived Worker. It registers Workflows and +Activities the same way. The difference is in the lifecycle: instead of the Worker starting and polling continuously, +Temporal invokes the Serverless Worker on demand, the Worker starts, processes available Tasks, and then shuts down. + +Serverless Workers require [Worker Versioning](/worker-versioning). Each Serverless Worker must be associated with a +[Worker Deployment Version](/worker-versioning#deployment-versions) that has a compute provider configured. + +To deploy a Serverless Worker, see +[Deploy a Serverless Worker](/production-deployment/worker-deployments/serverless-workers). + +## How Serverless invocation works {#how-invocation-works} + +With long-lived Workers, you start the Worker process, which connects to Temporal and polls a Task Queue for work. +Temporal does not need to know anything about the Worker's infrastructure. + +With Serverless Workers, Temporal starts the Worker. + +
+ +
Temporal's Worker Controller Instance invokes a Serverless Worker when Tasks arrive on a Task Queue with a compute provider configured.
+
+ +The invocation flow works as follows: + +1. A Task arrives on a Task Queue that has a compute provider configured. +2. Temporal's internal Worker Controller Instance (WCI) service detects that no Worker is polling the Task Queue. +3. Temporal uses the compute provider configuration to invoke the Serverless Worker. For example, calling AWS Lambda's + `InvokeFunction` API. +4. The Serverless Worker starts, creates a Temporal Client, and begins polling the Task Queue. +5. The Worker processes available Tasks until the invocation deadline approaches. +6. The Worker gracefully drains in-progress work and shuts down. + +Each invocation is independent. The Worker creates a fresh client connection on every invocation. There is no connection +reuse or shared state across invocations. + +## Worker lifecycle {#worker-lifecycle} + +A single Serverless Worker invocation has three phases: init, work, and shutdown. + +
+ +
The shutdown deadline buffer controls when the Worker stops polling, and the Worker stop timeout controls how long the Worker waits for in-flight Tasks to finish before shutdown hooks run.
+
+ +During the **init** phase, the Worker initializes and establishes a client connection to Temporal. + +During the **work** phase, the Worker polls the Task Queue and processes Tasks. + +During the **shutdown** phase, the Worker stops polling, waits for in-flight Tasks to finish, and runs any shutdown +hooks (for example, OpenTelemetry telemetry flushes). Shutdown begins before the invocation deadline so the Worker can +exit cleanly before the compute provider forcibly terminates the execution environment. + +### Tuning for long-running Activities + +If your Worker handles long-running Activities, set these three values together: + +- **Worker stop timeout > longest Activity runtime.** Gives in-flight Activities enough time to finish after polling + stops. +- **Shutdown deadline buffer > Worker stop timeout + shutdown hook time.** Ensures the drain and any shutdown hooks + complete before the compute provider terminates the environment. +- **Invocation deadline > longest Activity runtime + shutdown deadline buffer.** Set on the compute provider to give + each invocation enough total runtime. + +For example, if your longest Activity runtime is 5 minutes, and your shutdown hooks take 3 seconds to run, set the +Worker stop timeout to more than 5 minutes, and the shutdown deadline buffer to more than 303 seconds (5 minutes + 3 +seconds). Set your invocation deadline to at least 10 minutes and 3 seconds (5 minutes + 303 seconds). + +The Worker stop timeout controls how long the Worker waits for in-flight Tasks to finish after it stops polling. The +shutdown deadline buffer controls how much time before the invocation deadline the Worker stops polling for Tasks. + +Raising only the shutdown deadline buffer makes the Worker stop polling earlier, but does not give in-flight Tasks any +more time to complete. + +Raising only the Worker stop timeout does not make the Worker stop polling earlier, which means the compute provider +might terminate the Worker before the full stop timeout completes. In-flight Activities then do not get the full stop +timeout to finish, and the shutdown hooks may not run. + +## Compute providers {#compute-providers} + +A compute provider is the configuration that tells Temporal how to invoke a Serverless Worker. The compute provider is +set on a [Worker Deployment Version](/worker-versioning#deployment-versions) and specifies the provider type, the +invocation target, and the credentials Temporal needs to trigger the invocation. + +For example, an AWS Lambda compute provider includes the Lambda function ARN and the IAM role that Temporal assumes to +invoke the function. + +Compute providers are only needed for Serverless Workers. Traditional long-lived Workers do not require a compute +provider because the Worker process manages its own lifecycle. + +### Supported providers + +| Provider | Description | +| ---------- | ----------------------------------------------------------------------------- | +| AWS Lambda | Temporal assumes an IAM role in your AWS account to invoke a Lambda function. | diff --git a/docs/encyclopedia/workers/task-queues.mdx b/docs/encyclopedia/workers/task-queues.mdx index ce8d564802..67cdec4863 100644 --- a/docs/encyclopedia/workers/task-queues.mdx +++ b/docs/encyclopedia/workers/task-queues.mdx @@ -94,7 +94,7 @@ There are five places where the name of the Task Queue can be set by the develop - [How to run a development Worker using the Python SDK](/develop/python/workers/run-worker-process#run-a-dev-worker) - [How to run a development Worker using the TypeScript SDK](/develop/typescript/workers/run-worker-process#run-a-dev-worker) - [How to run a development Worker using the .NET SDK](/develop/dotnet/workers/run-worker-process)

- - [How to run a Temporal Cloud Worker using the Go SDK](/develop/go/workers/cloud-worker) + - [How to connect a Go SDK Worker to Temporal Cloud](/develop/go/workers/run-worker-process#connect-to-temporal-cloud) - [How to run a Temporal Cloud Worker using the TypeScript SDK](/develop/typescript/workers/run-worker-process#run-a-temporal-cloud-worker) Note that all Worker Entities listening to the same Task Queue name must be registered to handle the exact same Workflows Types, Activity Types, and Nexus Operations. diff --git a/docs/encyclopedia/workers/workers.mdx b/docs/encyclopedia/workers/workers.mdx index c152971628..03728f07f4 100644 --- a/docs/encyclopedia/workers/workers.mdx +++ b/docs/encyclopedia/workers/workers.mdx @@ -41,7 +41,7 @@ A Worker Program is the static code that defines the constraints of the Worker P - [How to run a development Worker using the TypeScript SDK](/develop/typescript/workers/run-worker-process#run-a-dev-worker) - [How to run a development Worker using the .NET SDK](/develop/dotnet/workers/run-worker-process) -- [How to run a Temporal Cloud Worker using the Go SDK](/develop/go/workers/cloud-worker) +- [How to connect a Go SDK Worker to Temporal Cloud](/develop/go/workers/run-worker-process#connect-to-temporal-cloud) - [How to run a Temporal Cloud Worker using the TypeScript SDK](/develop/typescript/workers/run-worker-process#run-a-temporal-cloud-worker) ::: diff --git a/sidebars.js b/sidebars.js index dbcb56f511..995dd27f9a 100644 --- a/sidebars.js +++ b/sidebars.js @@ -1396,6 +1396,7 @@ module.exports = { 'encyclopedia/workers/sticky-execution', 'encyclopedia/workers/worker-shutdown', 'encyclopedia/workers/worker-versioning', + 'encyclopedia/workers/serverless-workers', ], }, { diff --git a/static/diagrams/serverless-worker-flow-dark.svg b/static/diagrams/serverless-worker-flow-dark.svg new file mode 100644 index 0000000000..a13c90f1e8 --- /dev/null +++ b/static/diagrams/serverless-worker-flow-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/diagrams/serverless-worker-flow.svg b/static/diagrams/serverless-worker-flow.svg new file mode 100644 index 0000000000..aeea36b1f4 --- /dev/null +++ b/static/diagrams/serverless-worker-flow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/diagrams/serverless-worker-lifecycle-dark.svg b/static/diagrams/serverless-worker-lifecycle-dark.svg new file mode 100644 index 0000000000..07718b6ec3 --- /dev/null +++ b/static/diagrams/serverless-worker-lifecycle-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/diagrams/serverless-worker-lifecycle.svg b/static/diagrams/serverless-worker-lifecycle.svg new file mode 100644 index 0000000000..6587b0f5d2 --- /dev/null +++ b/static/diagrams/serverless-worker-lifecycle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/vale/styles/Temporal/terms.yml b/vale/styles/Temporal/terms.yml index 0504678fd2..ecf3921314 100644 --- a/vale/styles/Temporal/terms.yml +++ b/vale/styles/Temporal/terms.yml @@ -102,6 +102,8 @@ swap: temporal sdks: Temporal SDKs temporal server: Temporal Server '\bworker\b': Worker + serverless worker: Serverless Worker + serverless workers: Serverless Workers worker controller: Worker Controller '\bworkflow\b': Workflow timer: Timer From 15a4c680c6dccaa88800e6b32e9d5b4706c8590e Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Fri, 17 Apr 2026 15:49:13 -0700 Subject: [PATCH 05/27] docs: update deploy guide with UI steps and real CLI flags Co-Authored-By: Claude Opus 4.7 (1M context) --- .../serverless-workers/aws-lambda.mdx | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx index 49d6d65d50..eafd4d8457 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx @@ -299,27 +299,40 @@ You can create the version using the Temporal UI, the Temporal CLI, or programma Use the UI for one-off setup and exploration. -When you create a version through the UI, the version is automatically set as current. - - +1. In the Temporal UI, open your Namespace. +2. In the left pane, select **Workers**. +3. Click **Create Worker Deployment** in the upper right corner. +4. Under **Configuration**, enter a **Name** and **Build ID**. These must match the `DeploymentName` and `BuildID` in your Worker code. +5. Under **Compute**, select **AWS Lambda** and provide: + - **Lambda ARN**: the ARN of your Lambda function. + - **IAM Role ARN**: the role ARN from [Step 3](#configure-iam) (output of the CloudFormation stack). + - **External ID**: the same value you passed to the CloudFormation template. +6. Click **Save**. + +When you create a version through the UI, the version is automatically set as current. Skip to [Verify the deployment](#verify-the-deployment). ### Temporal CLI 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. - - ```bash temporal worker deployment create-version \ --namespace \ --deployment-name my-app \ --build-id build-1 \ - --aws-lambda-invoke arn:aws:lambda:::function:my-temporal-worker + --aws-lambda-function-arn arn:aws:lambda:::function:my-temporal-worker \ + --aws-lambda-assume-role-arn arn:aws:iam:::role/ \ + --aws-lambda-assume-role-external-id ``` +| Flag | Description | +|---|---| +| `--deployment-name` | Worker Deployment name. Must match `DeploymentName` in your Worker code. | +| `--build-id` | Worker Deployment Version build ID. Must match `BuildID` in your Worker code. | +| `--aws-lambda-function-arn` | ARN of the Lambda function Temporal invokes for this version. | +| `--aws-lambda-assume-role-arn` | IAM role Temporal assumes to invoke the function. This is the `RoleARN` output from the CloudFormation stack in [Step 3](#configure-iam). | +| `--aws-lambda-assume-role-external-id` | External ID configured in the IAM role trust policy. | + ### SDK 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. From 6a582f9bb7c3fd79cc23c829fe67a37704b3ab84 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Fri, 17 Apr 2026 15:51:58 -0700 Subject: [PATCH 06/27] docs: update lifecycle dark diagram with lighter borders Co-Authored-By: Claude Opus 4.7 (1M context) --- static/diagrams/serverless-worker-lifecycle-dark.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/diagrams/serverless-worker-lifecycle-dark.svg b/static/diagrams/serverless-worker-lifecycle-dark.svg index 07718b6ec3..4ed19ca017 100644 --- a/static/diagrams/serverless-worker-lifecycle-dark.svg +++ b/static/diagrams/serverless-worker-lifecycle-dark.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 39f68525549afa6bfa117be9d14ae102642607a9 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Fri, 17 Apr 2026 15:55:59 -0700 Subject: [PATCH 07/27] docs: tabify deploy approaches and drop UI positioning sentence Co-Authored-By: Claude Opus 4.7 (1M context) --- .../serverless-workers/aws-lambda.mdx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx index eafd4d8457..ffd865b4c2 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx @@ -19,6 +19,8 @@ tags: --- import SdkTabs from '@site/src/components/elements/SdkTabs'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; This guide walks through deploying a Temporal Worker on AWS Lambda. @@ -295,9 +297,8 @@ The deployment name and build ID must match the values in your Worker code. You can create the version using the Temporal UI, the Temporal CLI, or programmatically with an SDK. -### Temporal UI - -Use the UI for one-off setup and exploration. + + 1. In the Temporal UI, open your Namespace. 2. In the left pane, select **Workers**. @@ -311,7 +312,8 @@ Use the UI for one-off setup and exploration. When you create a version through the UI, the version is automatically set as current. Skip to [Verify the deployment](#verify-the-deployment). -### Temporal CLI + + 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. @@ -333,7 +335,8 @@ temporal worker deployment create-version \ | `--aws-lambda-assume-role-arn` | IAM role Temporal assumes to invoke the function. This is the `RoleARN` output from the CloudFormation stack in [Step 3](#configure-iam). | | `--aws-lambda-assume-role-external-id` | External ID configured in the IAM role trust policy. | -### SDK + + 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. @@ -370,6 +373,9 @@ client.CreateWorkerDeploymentVersion(ctx, &workflowservice.CreateWorkerDeploymen + + + ## 5. Set the version as current {#set-current-version} If you created the version through the Temporal UI, the version is already current and you can skip this step. From c0cd65b2de47405d45000d3ce8afa646d4d97c64 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Fri, 17 Apr 2026 16:17:24 -0700 Subject: [PATCH 08/27] docs: restructure deploy step with command-first layout and unified param table Co-Authored-By: Claude Opus 4.7 (1M context) --- .../serverless-workers/aws-lambda.mdx | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx index ffd865b4c2..5e7ad11c49 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx @@ -85,9 +85,7 @@ For details on configuration options, Lambda-tuned defaults, and the invocation ## 2. Deploy the Lambda function {#deploy-the-lambda-function} -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. -If you don't already have one, create a role with `lambda.amazonaws.com` as the trusted principal before proceeding. -This role is separate from the IAM role that Temporal uses to invoke the function. +Build your Worker for the Lambda runtime, package it as a zip, and deploy it to AWS Lambda. ### i. Build and package {#build-and-package} @@ -111,36 +109,39 @@ zip function.zip bootstrap ### ii. Deploy the Lambda function {#deploy-lambda-function} -Configure the Temporal connection using Lambda environment variables. -The `lambdaworker` package reads these automatically at startup. - -| Variable | Description | Required | -|---|---|---| -| `TEMPORAL_ADDRESS` | Temporal frontend address (e.g., `..tmprl.cloud:7233`). | Yes | -| `TEMPORAL_NAMESPACE` | Temporal Namespace. | Yes | -| `HOME` | Must be set to `/tmp` so the SDK's config loader can resolve a user config directory in the Lambda environment. | Yes | -| `TEMPORAL_TASK_QUEUE` | Task Queue name. Overrides the value set in code. | No | -| `TEMPORAL_TLS_CLIENT_CERT_PATH` | Path to the TLS client certificate file for mTLS authentication. | For mTLS | -| `TEMPORAL_TLS_CLIENT_KEY_PATH` | Path to the TLS client key file for mTLS authentication. | For mTLS | -| `TEMPORAL_API_KEY` | API key for API key authentication. | For API key auth | - -For the full list of supported environment variables, see [Client environment configuration](/references/client-environment-configuration). - -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. - ```bash aws lambda create-function \ --function-name my-temporal-worker \ --runtime provided.al2023 \ --handler bootstrap \ - --architectures x86_64 \ --role arn:aws:iam:::role/my-temporal-worker-execution \ --zip-file fileb://function.zip \ - --timeout 60 \ + --timeout 600 \ --memory-size 256 \ --environment "Variables={HOME=/tmp,TEMPORAL_ADDRESS=:7233,TEMPORAL_NAMESPACE=,TEMPORAL_API_KEY=}" ``` +| Parameter | Description | +|---|---| +| `--function-name` | Name of the Lambda function. | +| `--runtime` | Lambda runtime. Use `provided.al2023` for custom Go binaries. | +| `--handler` | Entry point binary name. Must be `bootstrap` when using the `provided.al2023` custom runtime. | +| `--role` | ARN of the Lambda [execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html), which grants the function permission to run. Trusted principal must be `lambda.amazonaws.com`. This is separate from the role Temporal uses to invoke the function in [Step 3](#configure-iam). | +| `--zip-file` | Path to your packaged deployment zip. | +| `--timeout` | Invocation deadline in seconds. This is the maximum time each Lambda invocation can run before AWS terminates it. Set this high enough for the Worker to start, process Tasks, and [shut down gracefully](/serverless-workers#worker-lifecycle). | +| `--memory-size` | Memory in MB allocated to each invocation. | +| `HOME` | Must be `/tmp`. | +| `TEMPORAL_ADDRESS` | Temporal frontend address (e.g., `..tmprl.cloud:7233`). | +| `TEMPORAL_NAMESPACE` | Temporal Namespace. | +| `TEMPORAL_TASK_QUEUE` | Task Queue name. Overrides the value set in code. | +| `TEMPORAL_TLS_CLIENT_CERT_PATH` | Path to the TLS client certificate file for mTLS authentication. | +| `TEMPORAL_TLS_CLIENT_KEY_PATH` | Path to the TLS client key file for mTLS authentication. | +| `TEMPORAL_API_KEY` | API key for API key authentication. | + +The `lambdaworker` package reads environment variables automatically at startup. For the full list, see [Client environment configuration](/references/client-environment-configuration). + +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. + To update an existing function with new code: ```bash From 58dab1cfece06975741ed07c8fcbdfb81eefd2e8 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Fri, 17 Apr 2026 16:20:30 -0700 Subject: [PATCH 09/27] docs: add ADOT layer reminder and long-running activity tuning link to Go SDK page Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/develop/go/workers/serverless-workers/aws-lambda.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/develop/go/workers/serverless-workers/aws-lambda.mdx b/docs/develop/go/workers/serverless-workers/aws-lambda.mdx index beaebd6e07..45cea73bd5 100644 --- a/docs/develop/go/workers/serverless-workers/aws-lambda.mdx +++ b/docs/develop/go/workers/serverless-workers/aws-lambda.mdx @@ -96,6 +96,9 @@ Eager Activities require a persistent connection, which Lambda invocations don't It controls how much time before the Lambda deadline the Worker begins its graceful shutdown. The default is `WorkerStopTimeout` + 2 seconds. +If your Worker handles long-running Activities, increase `WorkerStopTimeout`, `ShutdownDeadlineBuffer`, and the Lambda invocation deadline (`--timeout`) together. +For guidance on how these values relate, see [Tuning for long-running Activities](/serverless-workers#tuning-for-long-running-activities). + ## Add observability with OpenTelemetry {#add-observability} The `lambdaworker/otel` sub-package provides OpenTelemetry integration with defaults configured for the [AWS Distro for OpenTelemetry (ADOT)](https://aws-otel.github.io/docs/getting-started/lambda) Lambda layer. @@ -137,4 +140,7 @@ func main() { `ApplyDefaults` configures both metrics and tracing. By default, telemetry is sent to `localhost:4317`, which is the ADOT Lambda layer's default collector endpoint. +To collect this telemetry, attach the [AWS Distro for OpenTelemetry Lambda layer](https://aws-otel.github.io/docs/getting-started/lambda/lambda-go) to your Lambda function. +The layer runs a collector sidecar that receives telemetry on `localhost:4317` and forwards it to your configured backend (e.g., AWS X-Ray, Amazon CloudWatch). + If you only need metrics or tracing, use `otel.ApplyMetrics` or `otel.ApplyTracing` individually. From 6a2e2645238d6c2c4c5fa43ee55251bc54344263 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Tue, 21 Apr 2026 10:27:49 -0700 Subject: [PATCH 10/27] docs: add TLS customization example and execution role policy requirement Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/develop/go/workers/serverless-workers/aws-lambda.mdx | 8 ++++++++ .../worker-deployments/serverless-workers/aws-lambda.mdx | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/develop/go/workers/serverless-workers/aws-lambda.mdx b/docs/develop/go/workers/serverless-workers/aws-lambda.mdx index 45cea73bd5..3c2f9ed489 100644 --- a/docs/develop/go/workers/serverless-workers/aws-lambda.mdx +++ b/docs/develop/go/workers/serverless-workers/aws-lambda.mdx @@ -67,6 +67,14 @@ The `Options` callback gives you access to the same registration methods you use The `lambdaworker` package automatically loads Temporal client configuration from a TOML config file and environment variables. Refer to [Environment Configuration](/develop/environment-configuration) for more details. +To customize the TLS configuration, set `opts.ClientOptions.ConnectionOptions.TLS` in the options callback: + +```go +opts.ClientOptions.ConnectionOptions.TLS = &tls.Config{ + // Custom TLS settings, e.g., RootCAs for a private CA +} +``` + Encrypt sensitive values like TLS keys or API keys. Refer to [AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars-encryption.html) for options. ## Adjust Worker defaults for Lambda {#lambda-tuned-defaults} diff --git a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx index 5e7ad11c49..ea4f8e54bb 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx @@ -126,7 +126,7 @@ aws lambda create-function \ | `--function-name` | Name of the Lambda function. | | `--runtime` | Lambda runtime. Use `provided.al2023` for custom Go binaries. | | `--handler` | Entry point binary name. Must be `bootstrap` when using the `provided.al2023` custom runtime. | -| `--role` | ARN of the Lambda [execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html), which grants the function permission to run. Trusted principal must be `lambda.amazonaws.com`. This is separate from the role Temporal uses to invoke the function in [Step 3](#configure-iam). | +| `--role` | ARN of the Lambda [execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html), which grants the function permission to run. Trusted principal must be `lambda.amazonaws.com`. This is separate from the role Temporal uses to invoke the function in [Step 3](#configure-iam). The role must have at least the [`AWSLambdaBasicExecutionRole`](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaBasicExecutionRole.html) managed policy attached, which grants permission to write CloudWatch Logs. | | `--zip-file` | Path to your packaged deployment zip. | | `--timeout` | Invocation deadline in seconds. This is the maximum time each Lambda invocation can run before AWS terminates it. Set this high enough for the Worker to start, process Tasks, and [shut down gracefully](/serverless-workers#worker-lifecycle). | | `--memory-size` | Memory in MB allocated to each invocation. | From 80ce53e048c1af507cdbe33107b9bc9fe0187baa Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Tue, 21 Apr 2026 10:29:20 -0700 Subject: [PATCH 11/27] docs: simplify execution role policy wording Co-Authored-By: Claude Opus 4.7 (1M context) --- .../worker-deployments/serverless-workers/aws-lambda.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx index ea4f8e54bb..1f0ff5e44b 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx @@ -126,7 +126,7 @@ aws lambda create-function \ | `--function-name` | Name of the Lambda function. | | `--runtime` | Lambda runtime. Use `provided.al2023` for custom Go binaries. | | `--handler` | Entry point binary name. Must be `bootstrap` when using the `provided.al2023` custom runtime. | -| `--role` | ARN of the Lambda [execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html), which grants the function permission to run. Trusted principal must be `lambda.amazonaws.com`. This is separate from the role Temporal uses to invoke the function in [Step 3](#configure-iam). The role must have at least the [`AWSLambdaBasicExecutionRole`](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaBasicExecutionRole.html) managed policy attached, which grants permission to write CloudWatch Logs. | +| `--role` | ARN of the Lambda [execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html), which grants the function permission to run. Trusted principal must be `lambda.amazonaws.com`. This is separate from the role Temporal uses to invoke the function in [Step 3](#configure-iam). The role must have at least the [`AWSLambdaBasicExecutionRole`](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaBasicExecutionRole.html) managed policy attached. | | `--zip-file` | Path to your packaged deployment zip. | | `--timeout` | Invocation deadline in seconds. This is the maximum time each Lambda invocation can run before AWS terminates it. Set this high enough for the Worker to start, process Tasks, and [shut down gracefully](/serverless-workers#worker-lifecycle). | | `--memory-size` | Memory in MB allocated to each invocation. | From d9b45b58e4670eb9ccc78c5b131f944c94745660 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Tue, 21 Apr 2026 10:29:51 -0700 Subject: [PATCH 12/27] docs: inline TLS config in main worker code sample Co-Authored-By: Claude Opus 4.7 (1M context) --- .../go/workers/serverless-workers/aws-lambda.mdx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/docs/develop/go/workers/serverless-workers/aws-lambda.mdx b/docs/develop/go/workers/serverless-workers/aws-lambda.mdx index 3c2f9ed489..54658a0870 100644 --- a/docs/develop/go/workers/serverless-workers/aws-lambda.mdx +++ b/docs/develop/go/workers/serverless-workers/aws-lambda.mdx @@ -35,6 +35,8 @@ Pass a `WorkerDeploymentVersion` and a callback that registers your Workflows an package main import ( + "crypto/tls" + lambdaworker "go.temporal.io/sdk/contrib/aws/lambdaworker" "go.temporal.io/sdk/worker" "go.temporal.io/sdk/workflow" @@ -46,6 +48,7 @@ func main() { BuildID: "build-1", }, func(opts *lambdaworker.Options) error { opts.TaskQueue = "my-task-queue" + opts.ClientOptions.ConnectionOptions.TLS = &tls.Config{} opts.RegisterWorkflowWithOptions(MyWorkflow, workflow.RegisterOptions{ VersioningBehavior: workflow.VersioningBehaviorAutoUpgrade, @@ -67,14 +70,6 @@ The `Options` callback gives you access to the same registration methods you use The `lambdaworker` package automatically loads Temporal client configuration from a TOML config file and environment variables. Refer to [Environment Configuration](/develop/environment-configuration) for more details. -To customize the TLS configuration, set `opts.ClientOptions.ConnectionOptions.TLS` in the options callback: - -```go -opts.ClientOptions.ConnectionOptions.TLS = &tls.Config{ - // Custom TLS settings, e.g., RootCAs for a private CA -} -``` - Encrypt sensitive values like TLS keys or API keys. Refer to [AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars-encryption.html) for options. ## Adjust Worker defaults for Lambda {#lambda-tuned-defaults} From e85c1cd146f76c4a6b8090093d12a467d8a955e5 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Tue, 21 Apr 2026 13:33:20 -0700 Subject: [PATCH 13/27] docs: add TLS line --- docs/develop/go/workers/serverless-workers/aws-lambda.mdx | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/develop/go/workers/serverless-workers/aws-lambda.mdx b/docs/develop/go/workers/serverless-workers/aws-lambda.mdx index 54658a0870..45cea73bd5 100644 --- a/docs/develop/go/workers/serverless-workers/aws-lambda.mdx +++ b/docs/develop/go/workers/serverless-workers/aws-lambda.mdx @@ -35,8 +35,6 @@ Pass a `WorkerDeploymentVersion` and a callback that registers your Workflows an package main import ( - "crypto/tls" - lambdaworker "go.temporal.io/sdk/contrib/aws/lambdaworker" "go.temporal.io/sdk/worker" "go.temporal.io/sdk/workflow" @@ -48,7 +46,6 @@ func main() { BuildID: "build-1", }, func(opts *lambdaworker.Options) error { opts.TaskQueue = "my-task-queue" - opts.ClientOptions.ConnectionOptions.TLS = &tls.Config{} opts.RegisterWorkflowWithOptions(MyWorkflow, workflow.RegisterOptions{ VersioningBehavior: workflow.VersioningBehaviorAutoUpgrade, From d16544ae7db7301cd7c6bdd04665b99cc463a651 Mon Sep 17 00:00:00 2001 From: Brandon Chavis Date: Tue, 21 Apr 2026 18:01:17 -0400 Subject: [PATCH 14/27] docs: Serverless Worker Autoscaling encyclopedia page (5/4) (#4460) * docs: add Serverless Worker Autoscaling encyclopedia page Add a dedicated page explaining how Temporal autoscales Serverless Workers on AWS Lambda, covering scaling signals (backlog + sync match rate), the push-based scaling flow, failure handling, and key constraints. Also adds an Autoscaling section to the existing Serverless Workers encyclopedia page linking to the new page, and a sidebar entry. Co-Authored-By: Claude Opus 4.6 (1M context) * merge autoscaling info with existing docs * copyedits * copyedits --------- Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: Lenny Chen --- .../workers/serverless-workers.mdx | 97 +++++++++++++++++-- 1 file changed, 88 insertions(+), 9 deletions(-) diff --git a/docs/encyclopedia/workers/serverless-workers.mdx b/docs/encyclopedia/workers/serverless-workers.mdx index d0c7a24933..27af3942a9 100644 --- a/docs/encyclopedia/workers/serverless-workers.mdx +++ b/docs/encyclopedia/workers/serverless-workers.mdx @@ -23,7 +23,10 @@ This page covers the following: - [What is a Serverless Worker?](#serverless-worker) - [How Serverless invocation works](#how-invocation-works) +- [Autoscaling](#autoscaling) - [Worker lifecycle](#worker-lifecycle) +- [Failure handling](#failure-handling) +- [Constraints](#constraints) - [Compute providers](#compute-providers) ## What is a Serverless Worker? {#serverless-worker} @@ -57,22 +60,51 @@ With Serverless Workers, Temporal starts the Worker. dark: '/diagrams/serverless-worker-flow-dark.svg', }} /> -
Temporal's Worker Controller Instance invokes a Serverless Worker when Tasks arrive on a Task Queue with a compute provider configured.
+
+ Temporal's Worker Controller Instance invokes a Serverless Worker when Tasks arrive on a Task Queue with a compute + provider configured. +
+Temporal's internal Worker Controller Instance (WCI) decides when to start, scale, and stop compute invocations. + The invocation flow works as follows: -1. A Task arrives on a Task Queue that has a compute provider configured. -2. Temporal's internal Worker Controller Instance (WCI) service detects that no Worker is polling the Task Queue. -3. Temporal uses the compute provider configuration to invoke the Serverless Worker. For example, calling AWS Lambda's - `InvokeFunction` API. -4. The Serverless Worker starts, creates a Temporal Client, and begins polling the Task Queue. -5. The Worker processes available Tasks until the invocation deadline approaches. -6. The Worker gracefully drains in-progress work and shuts down. +1. A Task is submitted (for example, `StartWorkflow` or `ScheduleActivity`). +2. The [Matching Service](/temporal-service/temporal-server#matching-service) attempts to route the Task directly to an + available Worker (a sync match). +3. If a Worker is available, the Task is routed to that Worker. +4. If no Worker is available (sync match fails), the Matching Service pushes a signal to the WCI, and the WCI invokes + the configured compute provider (for example, calling AWS Lambda's `InvokeFunction` API). +5. The Serverless Worker starts, creates a Temporal Client, and begins polling the Task Queue. +6. The Worker processes available Tasks until it exits (see [Worker lifecycle](#worker-lifecycle)). + +The WCI also monitors the Task Queue backlog independently. If tasks arrive faster than Workers can process them, the +WCI invokes additional Workers in parallel until the backlog drains or provider concurrency limits are reached. Each invocation is independent. The Worker creates a fresh client connection on every invocation. There is no connection reuse or shared state across invocations. +## Autoscaling {#autoscaling} + +Temporal automatically scales Serverless Workers based on Task Queue signals. When Tasks arrive and no Worker is +available, Temporal invokes new Workers. When the work is done, Workers exit and scale to zero. + +The WCI uses two signals to decide when to invoke new Workers: + +### Sync match failure {#sync-match-failure} + +When a Task is submitted, the [Matching Service](/temporal-service/temporal-server#matching-service) attempts to route +it directly to an available Worker. If no Worker is available, the sync match fails, and the Matching Service pushes a +signal to the WCI. The WCI then invokes a new Worker. This is the primary scaling path. Because the Matching Service +pushes match failures to the WCI as they happen rather than the WCI polling on a timer, latency stays low and scaling is +responsive. + +### Task Queue backlog {#task-queue-backlog} + +The WCI monitors Task Queue metadata to determine whether pending Tasks exist without enough Workers to process them. If +there is work on the queue and not enough Workers, the WCI invokes additional Workers. + ## Worker lifecycle {#worker-lifecycle} A single Serverless Worker invocation has three phases: init, work, and shutdown. @@ -85,7 +117,10 @@ A single Serverless Worker invocation has three phases: init, work, and shutdown dark: '/diagrams/serverless-worker-lifecycle-dark.svg', }} /> -
The shutdown deadline buffer controls when the Worker stops polling, and the Worker stop timeout controls how long the Worker waits for in-flight Tasks to finish before shutdown hooks run.
+
+ The shutdown deadline buffer controls when the Worker stops polling, and the Worker stop timeout controls how long + the Worker waits for in-flight Tasks to finish before shutdown hooks run. +
During the **init** phase, the Worker initializes and establishes a client connection to Temporal. @@ -121,6 +156,50 @@ Raising only the Worker stop timeout does not make the Worker stop polling earli might terminate the Worker before the full stop timeout completes. In-flight Activities then do not get the full stop timeout to finish, and the shutdown hooks may not run. +## Failure handling {#failure-handling} + +Serverless Workers rely on Temporal's standard retry and timeout semantics to recover from failures. The following +sections describe common failure scenarios and how they are handled. + +### Worker crash {#worker-crash} + +If a Worker invocation crashes (out of memory, unhandled exception, etc.), the behavior follows standard Temporal retry +semantics: + +- The Activity Timeout fires after the configured duration. +- Temporal retries the Activity on a different Worker invocation. +- No manual intervention is required. + +### Provider concurrency limit {#provider-concurrency-limit} + +If the compute provider's concurrency limit is reached (for example, AWS Lambda account concurrency): + +- Further invocations from the WCI fail. +- Tasks remain in the Task Queue backlog. No data loss occurs. +- Processing slows until concurrency frees up. + +### Resource exhaustion across Activity slots {#resource-exhaustion} + +By default, a single Worker invocation may run multiple Activity slots. A crash or resource exhaustion in one Activity +(for example, out-of-memory from a memory-intensive operation) can affect other Activities running in the same +invocation. + +To isolate Activities from each other: + +- Split Workflow and Activity Workers into separate compute functions. +- Set Activity slots to 1 per invocation. + +With single-slot configuration, each Activity gets a dedicated execution environment. + +## Constraints {#constraints} + +| Constraint | Detail | +| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| Activity duration | Must complete within the compute provider's invocation limit (minus shutdown deadline buffer). For AWS Lambda, the maximum is 15 minutes. | +| Workflow duration | No limit. Workflows of any duration work, regardless of the invocation timeout. A Workflow runs across as many invocations as needed. | +| Worker code | Same Temporal SDK Worker code, using the serverless Worker package for your SDK. | +| Versioning | [Worker Versioning](/worker-versioning) is required. Each Workflow must declare `AutoUpgrade` or `Pinned` behavior. | + ## Compute providers {#compute-providers} A compute provider is the configuration that tells Temporal how to invoke a Serverless Worker. The compute provider is From 815079b6bdd9f15e6be87b18ccb4c6c045e0c006 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Tue, 21 Apr 2026 15:21:40 -0700 Subject: [PATCH 15/27] remove SDK from creating version --- .../serverless-workers/aws-lambda.mdx | 42 +------------------ 1 file changed, 2 insertions(+), 40 deletions(-) diff --git a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx index 1f0ff5e44b..5f7a5212c0 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx @@ -296,7 +296,7 @@ Create a [Worker Deployment Version](/production-deployment/worker-deployments/w 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. The deployment name and build ID must match the values in your Worker code. -You can create the version using the Temporal UI, the Temporal CLI, or programmatically with an SDK. +You can create the version using the Temporal UI or the Temporal CLI. @@ -336,44 +336,6 @@ temporal worker deployment create-version \ | `--aws-lambda-assume-role-arn` | IAM role Temporal assumes to invoke the function. This is the `RoleARN` output from the CloudFormation stack in [Step 3](#configure-iam). | | `--aws-lambda-assume-role-external-id` | External ID configured in the IAM role trust policy. | - - - -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. - - - - -```go -// Create the worker deployment -client.CreateWorkerDeployment(ctx, &workflowservice.CreateWorkerDeploymentRequest{ - Namespace: "default", - DeploymentName: "my-app", -}) - -// Create a version with Lambda compute config -client.CreateWorkerDeploymentVersion(ctx, &workflowservice.CreateWorkerDeploymentVersionRequest{ - Namespace: "default", - DeploymentVersion: &deploymentpb.WorkerDeploymentVersion{ - DeploymentName: "my-app", - BuildId: "build-1", - }, - ComputeConfig: &computepb.ComputeConfig{ - ScalingGroups: map[string]*computepb.ComputeConfigScalingGroup{ - "default": { - Provider: &computepb.ComputeProvider{ - Type: "aws-lambda", - Details: /* Lambda ARN, role ARN, external ID */, - }, - }, - }, - }, -}) -``` - - - - @@ -381,7 +343,7 @@ client.CreateWorkerDeploymentVersion(ctx, &workflowservice.CreateWorkerDeploymen If you created the version through the Temporal UI, the version is already current and you can skip this step. -If you used the CLI or SDK, set the version as current. +If you used the CLI, set the version as current. Without this step, tasks on the Task Queue will not route to the version, and Temporal will not invoke the Lambda function. ```bash From bade7245a9401fd4c31af73840df447f1630309c Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Wed, 22 Apr 2026 14:46:08 -0700 Subject: [PATCH 16/27] copyedits --- .../serverless-workers/aws-lambda.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx index 5f7a5212c0..505e274207 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx @@ -22,7 +22,7 @@ import SdkTabs from '@site/src/components/elements/SdkTabs'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -This guide walks through deploying a Temporal Worker on AWS Lambda. +This guide walks through deploying a Temporal [Serverless Worker](/serverless-workers) on AWS Lambda. ## Prerequisites {#prerequisites} @@ -40,7 +40,7 @@ This guide walks through deploying a Temporal Worker on AWS Lambda. -## 1. Write the Worker code {#write-the-worker-code} +## 1. Write Worker code {#write-worker-code} Write a Worker that runs inside a Lambda function. The Worker handles the per-invocation lifecycle: connecting to Temporal, polling for tasks, and gracefully shutting down before the invocation deadline. @@ -83,7 +83,7 @@ For details on configuration options, Lambda-tuned defaults, and the invocation -## 2. Deploy the Lambda function {#deploy-the-lambda-function} +## 2. Deploy Lambda function {#deploy-lambda-function} Build your Worker for the Lambda runtime, package it as a zip, and deploy it to AWS Lambda. @@ -107,7 +107,7 @@ zip function.zip bootstrap -### ii. Deploy the Lambda function {#deploy-lambda-function} +### ii. Deploy Lambda function {#deploy-lambda-function-step} ```bash aws lambda create-function \ @@ -290,7 +290,7 @@ aws cloudformation create-stack \ The stack output `RoleARN` contains the IAM role ARN to use in your Worker Deployment Version's compute configuration. -## 4. Create a Worker Deployment Version {#create-worker-deployment-version} +## 4. Create Worker Deployment Version {#create-worker-deployment-version} Create a [Worker Deployment Version](/production-deployment/worker-deployments/worker-versioning) with a compute provider that points to your Lambda function. 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. @@ -339,7 +339,7 @@ temporal worker deployment create-version \ -## 5. Set the version as current {#set-current-version} +## 5. Set version as current {#set-current-version} If you created the version through the Temporal UI, the version is already current and you can skip this step. @@ -352,7 +352,7 @@ temporal worker deployment set-current-version \ --build-id build-1 ``` -## 6. Verify the deployment {#verify-the-deployment} +## 6. Verify deployment {#verify-deployment} Start a Workflow on the same Task Queue to confirm that Temporal invokes your Lambda Worker. From 9cbc085b616ce0fd971f3a4492dd477b4864181c Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Wed, 22 Apr 2026 18:06:01 -0700 Subject: [PATCH 17/27] docs: add describe-stacks command to retrieve role ARN from CloudFormation Co-Authored-By: Claude Opus 4.6 (1M context) --- .../worker-deployments/serverless-workers/aws-lambda.mdx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx index 505e274207..74b7306851 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx @@ -288,7 +288,13 @@ aws cloudformation create-stack \ --region ``` -The stack output `RoleARN` contains the IAM role ARN to use in your Worker Deployment Version's compute configuration. +After the stack finishes creating, retrieve the IAM role ARN from the stack outputs: + +```bash +aws cloudformation describe-stacks --stack-name --query 'Stacks[0].Outputs[?OutputKey==`RoleARN`].OutputValue' --output text --region +``` + +Use this role ARN in your Worker Deployment Version's compute configuration. ## 4. Create Worker Deployment Version {#create-worker-deployment-version} From 8a81056273410cf095cc4c048781a209eb5e7d9c Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Thu, 23 Apr 2026 12:28:11 -0700 Subject: [PATCH 18/27] docs: add scaling with long-lived Workers section to serverless encyclopedia Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/encyclopedia/workers/serverless-workers.mdx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/encyclopedia/workers/serverless-workers.mdx b/docs/encyclopedia/workers/serverless-workers.mdx index 27af3942a9..c15602b48d 100644 --- a/docs/encyclopedia/workers/serverless-workers.mdx +++ b/docs/encyclopedia/workers/serverless-workers.mdx @@ -24,6 +24,7 @@ This page covers the following: - [What is a Serverless Worker?](#serverless-worker) - [How Serverless invocation works](#how-invocation-works) - [Autoscaling](#autoscaling) +- [Scaling with long-lived Workers](#scaling-with-long-lived-workers) - [Worker lifecycle](#worker-lifecycle) - [Failure handling](#failure-handling) - [Constraints](#constraints) @@ -105,6 +106,16 @@ responsive. The WCI monitors Task Queue metadata to determine whether pending Tasks exist without enough Workers to process them. If there is work on the queue and not enough Workers, the WCI invokes additional Workers. +## Scaling with long-lived Workers {#scaling-with-long-lived-workers} + +Serverless Workers can share a Task Queue with long-lived Workers. +Because Serverless Workers are only invoked on [sync match failure](#sync-match-failure), Serverless Workers only pick up Tasks that no long-lived Worker was available to handle. +In practice, the Serverless Workers act as spillover capacity for the long-lived fleet. + +If you configure Serverless and long-lived Workers on the same Task Queue, do not enable dynamic scaling on the long-lived Workers. +The two groups cannot coordinate their scaling behavior. +If both scale dynamically, the long-lived Workers may scale up to handle the same Tasks that Temporal is simultaneously invoking Serverless Workers for, leading to unnecessary invocations and unpredictable scaling. + ## Worker lifecycle {#worker-lifecycle} A single Serverless Worker invocation has three phases: init, work, and shutdown. From 694c1d1d7c1638d7515877ea0c6bab3808fff922 Mon Sep 17 00:00:00 2001 From: Lenny Chen <55669665+lennessyy@users.noreply.github.com> Date: Tue, 28 Apr 2026 10:58:37 -0700 Subject: [PATCH 19/27] docs: self-hosted setup for serverless workers (#4476) * docs: add self-hosted setup page for serverless workers Covers enabling the Worker Controller via dynamic config, configuring AWS credentials for the Temporal server, and creating the Lambda invocation role with a CloudFormation template adapted for self-hosted. Co-Authored-By: Claude Opus 4.6 (1M context) * docs: add describe-stacks command to retrieve role ARN Co-Authored-By: Claude Opus 4.6 (1M context) * docs: add WCI verification steps and ARN discovery tip Add how to view WCI workflows using TemporalNamespaceDivision filter to both the self-hosted setup page and the deploy guide. Add aws sts get-caller-identity tip for finding the server's IAM ARN. Co-Authored-By: Claude Opus 4.6 (1M context) * docs: remove WCI filter tip from deploy guide Co-Authored-By: Claude Opus 4.6 (1M context) * docs: mark IAM section as Cloud-only, link to self-hosted setup Co-Authored-By: Claude Opus 4.6 (1M context) * docs: add (Cloud only) to IAM heading Co-Authored-By: Claude Opus 4.6 (1M context) * docs: move Cloud-only callout to top of IAM section Co-Authored-By: Claude Opus 4.6 (1M context) * docs: address PR review feedback on self-hosted setup page - Remove first paragraph (Cloud comparison) - Add brief overview of the three setup steps - Use "Worker Controller Instance (WCI)" instead of "Worker Controller" - Move dynamic config reference table out (belongs on dynamic config page) - Replace verify section with Next steps linking to deploy guide Co-Authored-By: Claude Opus 4.6 (1M context) * prettier * docs: reference self-hosted setup page in deploy guide prerequisites Co-Authored-By: Claude Opus 4.6 (1M context) * Apply suggestions from code review Co-authored-by: Stefan Richter * Apply suggestions from code review Co-authored-by: Stefan Richter * copyedits * copyedits --------- Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: Stefan Richter --- .../serverless-workers/aws-lambda.mdx | 155 +++++++------ .../serverless-workers/self-hosted-setup.mdx | 210 ++++++++++++++++++ sidebars.js | 1 + ...al-self-hosted-serverless-worker-role.yaml | 74 ++++++ 4 files changed, 368 insertions(+), 72 deletions(-) create mode 100644 docs/production-deployment/worker-deployments/serverless-workers/self-hosted-setup.mdx create mode 100644 static/files/temporal-self-hosted-serverless-worker-role.yaml diff --git a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx index 74b7306851..158ca2bf07 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx @@ -27,10 +27,12 @@ This guide walks through deploying a Temporal [Serverless Worker](/serverless-wo ## Prerequisites {#prerequisites} - A Temporal Cloud account or a self-hosted Temporal Service vx.xx.x or later. - - 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. +- For self-hosted deployments, complete the + [self-hosted setup](/production-deployment/worker-deployments/serverless-workers/self-hosted-setup) before following + this guide. - An AWS account with permissions to create and invoke Lambda functions and create IAM roles. -- 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. - +- 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. @@ -42,8 +44,8 @@ This guide walks through deploying a Temporal [Serverless Worker](/serverless-wo ## 1. Write Worker code {#write-worker-code} -Write a Worker that runs inside a Lambda function. -The Worker handles the per-invocation lifecycle: connecting to Temporal, polling for tasks, and gracefully shutting down before the invocation deadline. +Write a Worker that runs inside a Lambda function. The Worker handles the per-invocation lifecycle: connecting to +Temporal, polling for tasks, and gracefully shutting down before the invocation deadline. @@ -76,9 +78,11 @@ func main() { } ``` -Each Workflow must declare a [versioning behavior](/worker-versioning#versioning-behaviors) at registration time, either `AutoUpgrade` or `Pinned`. +Each Workflow must declare a [versioning behavior](/worker-versioning#versioning-behaviors) at registration time, either +`AutoUpgrade` or `Pinned`. -For details on configuration options, Lambda-tuned defaults, and the invocation lifecycle, see [Serverless Workers - Go SDK](/develop/go/workers/serverless-workers/aws-lambda). +For details on configuration options, Lambda-tuned defaults, and the invocation lifecycle, see +[Serverless Workers - Go SDK](/develop/go/workers/serverless-workers/aws-lambda). @@ -121,26 +125,28 @@ aws lambda create-function \ --environment "Variables={HOME=/tmp,TEMPORAL_ADDRESS=:7233,TEMPORAL_NAMESPACE=,TEMPORAL_API_KEY=}" ``` -| Parameter | Description | -|---|---| -| `--function-name` | Name of the Lambda function. | -| `--runtime` | Lambda runtime. Use `provided.al2023` for custom Go binaries. | -| `--handler` | Entry point binary name. Must be `bootstrap` when using the `provided.al2023` custom runtime. | -| `--role` | ARN of the Lambda [execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html), which grants the function permission to run. Trusted principal must be `lambda.amazonaws.com`. This is separate from the role Temporal uses to invoke the function in [Step 3](#configure-iam). The role must have at least the [`AWSLambdaBasicExecutionRole`](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaBasicExecutionRole.html) managed policy attached. | -| `--zip-file` | Path to your packaged deployment zip. | -| `--timeout` | Invocation deadline in seconds. This is the maximum time each Lambda invocation can run before AWS terminates it. Set this high enough for the Worker to start, process Tasks, and [shut down gracefully](/serverless-workers#worker-lifecycle). | -| `--memory-size` | Memory in MB allocated to each invocation. | -| `HOME` | Must be `/tmp`. | -| `TEMPORAL_ADDRESS` | Temporal frontend address (e.g., `..tmprl.cloud:7233`). | -| `TEMPORAL_NAMESPACE` | Temporal Namespace. | -| `TEMPORAL_TASK_QUEUE` | Task Queue name. Overrides the value set in code. | -| `TEMPORAL_TLS_CLIENT_CERT_PATH` | Path to the TLS client certificate file for mTLS authentication. | -| `TEMPORAL_TLS_CLIENT_KEY_PATH` | Path to the TLS client key file for mTLS authentication. | -| `TEMPORAL_API_KEY` | API key for API key authentication. | - -The `lambdaworker` package reads environment variables automatically at startup. For the full list, see [Client environment configuration](/references/client-environment-configuration). - -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. +| Parameter | Description | +| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--function-name` | Name of the Lambda function. | +| `--runtime` | Lambda runtime. Use `provided.al2023` for custom Go binaries. | +| `--handler` | Entry point binary name. Must be `bootstrap` when using the `provided.al2023` custom runtime. | +| `--role` | ARN of the Lambda [execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html), which grants the function permission to run. Trusted principal must be `lambda.amazonaws.com`. This is separate from the role Temporal uses to invoke the function in [Step 3](#configure-iam). The role must have at least the [`AWSLambdaBasicExecutionRole`](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaBasicExecutionRole.html) managed policy attached. | +| `--zip-file` | Path to your packaged deployment zip. | +| `--timeout` | Invocation deadline in seconds. This is the maximum time each Lambda invocation can run before AWS terminates it. Set this high enough for the Worker to start, process Tasks, and [shut down gracefully](/serverless-workers#worker-lifecycle). | +| `--memory-size` | Memory in MB allocated to each invocation. | +| `HOME` | Must be `/tmp`. | +| `TEMPORAL_ADDRESS` | Temporal frontend address (e.g., `..tmprl.cloud:7233`). | +| `TEMPORAL_NAMESPACE` | Temporal Namespace. | +| `TEMPORAL_TASK_QUEUE` | Task Queue name. Overrides the value set in code. | +| `TEMPORAL_TLS_CLIENT_CERT_PATH` | Path to the TLS client certificate file for mTLS authentication. | +| `TEMPORAL_TLS_CLIENT_KEY_PATH` | Path to the TLS client key file for mTLS authentication. | +| `TEMPORAL_API_KEY` | API key for API key authentication. | + +The `lambdaworker` package reads environment variables automatically at startup. For the full list, see +[Client environment configuration](/references/client-environment-configuration). + +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. To update an existing function with new code: @@ -150,25 +156,24 @@ aws lambda update-function-code \ --zip-file fileb://function.zip ``` -## 3. Configure IAM for Temporal invocation {#configure-iam} - -Temporal needs permission to invoke your Lambda function. -The Temporal server assumes an IAM role in your AWS account to call `lambda:InvokeFunction`. -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. - -Deploy the following CloudFormation template to create the invocation role and its permissions. [Download the template](/files/temporal-cloud-serverless-worker-role.yaml). +## 3. Configure IAM for Temporal invocation (Cloud only) {#configure-iam} -:::note +This section applies to Temporal Cloud. For self-hosted Temporal Service deployments, see +[Self-hosted setup](/production-deployment/worker-deployments/serverless-workers/self-hosted-setup#create-invocation-role) +for IAM configuration with a different CloudFormation template. -This template is scoped to Temporal Cloud. Self-hosted configuration guidance is in progress. +Temporal needs permission to invoke your Lambda function. The Temporal server assumes an IAM role in your AWS account to +call `lambda:InvokeFunction`. 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. -::: +Deploy the following CloudFormation template to create the invocation role and its permissions. +[Download the template](/files/temporal-cloud-serverless-worker-role.yaml). -| Parameter | Description | -|---|---| -| `AssumeRoleExternalId` | A unique identifier that Temporal Cloud presents when assuming the role. Provided in your Namespace configuration. | -| `LambdaFunctionARNs` | Comma-separated list of Lambda function ARNs that Temporal may invoke. One role can authorize multiple Worker Lambdas. | -| `RoleName` | Base name for the created IAM role. Defaults to `Temporal-Cloud-Serverless-Worker`. | +| Parameter | Description | +| ---------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| `AssumeRoleExternalId` | A unique identifier that Temporal Cloud presents when assuming the role. Provided in your Namespace configuration. | +| `LambdaFunctionARNs` | Comma-separated list of Lambda function ARNs that Temporal may invoke. One role can authorize multiple Worker Lambdas. | +| `RoleName` | Base name for the created IAM role. Defaults to `Temporal-Cloud-Serverless-Worker`. |
CloudFormation template @@ -176,7 +181,8 @@ This template is scoped to Temporal Cloud. Self-hosted configuration guidance is ```yaml # CloudFormation template for creating an IAM role that Temporal Cloud can assume to invoke Lambda functions. AWSTemplateFormatVersion: '2010-09-09' -Description: Creates an IAM role that Temporal Cloud can assume to invoke multiple Lambda functions for Serverless Workers. +Description: + Creates an IAM role that Temporal Cloud can assume to invoke multiple Lambda functions for Serverless Workers. Parameters: AssumeRoleExternalId: @@ -189,8 +195,8 @@ Parameters: LambdaFunctionARNs: Type: CommaDelimitedList Description: >- - Comma-separated list of Lambda function ARNs to invoke - (e.g., arn:aws:lambda:us-west-2:123456789012:function:worker-1,arn:aws:lambda:us-west-2:123456789012:function:worker-2) + Comma-separated list of Lambda function ARNs to invoke (e.g., + arn:aws:lambda:us-west-2:123456789012:function:worker-1,arn:aws:lambda:us-west-2:123456789012:function:worker-2) RoleName: Type: String @@ -200,21 +206,21 @@ Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: - default: "Temporal Cloud Configuration" + default: 'Temporal Cloud Configuration' Parameters: - AssumeRoleExternalId - Label: - default: "Lambda Configuration" + default: 'Lambda Configuration' Parameters: - LambdaFunctionARNs - RoleName ParameterLabels: AssumeRoleExternalId: - default: "External ID (provided by Temporal Cloud)" + default: 'External ID (provided by Temporal Cloud)' LambdaFunctionARNs: - default: "Lambda Function ARNs (comma-separated list)" + default: 'Lambda Function ARNs (comma-separated list)' RoleName: - default: "IAM Role Name" + default: 'IAM Role Name' Resources: TemporalCloudServerlessWorker: @@ -232,13 +238,13 @@ Resources: arn:aws:iam::160190466495:role/wci-lambda-invoke, arn:aws:iam::819232936619:role/wci-lambda-invoke, arn:aws:iam::829909441867:role/wci-lambda-invoke, - arn:aws:iam::354116250941:role/wci-lambda-invoke + arn:aws:iam::354116250941:role/wci-lambda-invoke, ] Action: sts:AssumeRole Condition: StringEquals: 'sts:ExternalId': [!Ref AssumeRoleExternalId] - Description: "The role Temporal Cloud uses to invoke Lambda functions for Serverless Workers" + Description: 'The role Temporal Cloud uses to invoke Lambda functions for Serverless Workers' MaxSessionDuration: 3600 # 1 hour TemporalCloudLambdaInvokePermissions: @@ -262,7 +268,7 @@ Outputs: Description: The ARN of the IAM role created for Temporal Cloud Value: !GetAtt TemporalCloudServerlessWorker.Arn Export: - Name: !Sub "${AWS::StackName}-RoleARN" + Name: !Sub '${AWS::StackName}-RoleARN' RoleName: Description: The name of the IAM role @@ -270,7 +276,7 @@ Outputs: LambdaFunctionARNs: Description: The Lambda function ARNs that can be invoked - Value: !Join [", ", !Ref LambdaFunctionARNs] + Value: !Join [', ', !Ref LambdaFunctionARNs] ```
@@ -298,9 +304,10 @@ Use this role ARN in your Worker Deployment Version's compute configuration. ## 4. Create Worker Deployment Version {#create-worker-deployment-version} -Create a [Worker Deployment Version](/production-deployment/worker-deployments/worker-versioning) with a compute provider that points to your Lambda function. -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. -The deployment name and build ID must match the values in your Worker code. +Create a [Worker Deployment Version](/production-deployment/worker-deployments/worker-versioning) with a compute +provider that points to your Lambda function. 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. The deployment name and build ID must +match the values in your Worker code. You can create the version using the Temporal UI or the Temporal CLI. @@ -310,19 +317,22 @@ You can create the version using the Temporal UI or the Temporal CLI. 1. In the Temporal UI, open your Namespace. 2. In the left pane, select **Workers**. 3. Click **Create Worker Deployment** in the upper right corner. -4. Under **Configuration**, enter a **Name** and **Build ID**. These must match the `DeploymentName` and `BuildID` in your Worker code. +4. Under **Configuration**, enter a **Name** and **Build ID**. These must match the `DeploymentName` and `BuildID` in + your Worker code. 5. Under **Compute**, select **AWS Lambda** and provide: - **Lambda ARN**: the ARN of your Lambda function. - **IAM Role ARN**: the role ARN from [Step 3](#configure-iam) (output of the CloudFormation stack). - **External ID**: the same value you passed to the CloudFormation template. 6. Click **Save**. -When you create a version through the UI, the version is automatically set as current. Skip to [Verify the deployment](#verify-the-deployment). +When you create a version through the UI, the version is automatically set as current. Skip to +[Verify the deployment](#verify-the-deployment). -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. +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. ```bash temporal worker deployment create-version \ @@ -334,13 +344,13 @@ temporal worker deployment create-version \ --aws-lambda-assume-role-external-id ``` -| Flag | Description | -|---|---| -| `--deployment-name` | Worker Deployment name. Must match `DeploymentName` in your Worker code. | -| `--build-id` | Worker Deployment Version build ID. Must match `BuildID` in your Worker code. | -| `--aws-lambda-function-arn` | ARN of the Lambda function Temporal invokes for this version. | -| `--aws-lambda-assume-role-arn` | IAM role Temporal assumes to invoke the function. This is the `RoleARN` output from the CloudFormation stack in [Step 3](#configure-iam). | -| `--aws-lambda-assume-role-external-id` | External ID configured in the IAM role trust policy. | +| Flag | Description | +| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `--deployment-name` | Worker Deployment name. Must match `DeploymentName` in your Worker code. | +| `--build-id` | Worker Deployment Version build ID. Must match `BuildID` in your Worker code. | +| `--aws-lambda-function-arn` | ARN of the Lambda function Temporal invokes for this version. | +| `--aws-lambda-assume-role-arn` | IAM role Temporal assumes to invoke the function. This is the `RoleARN` output from the CloudFormation stack in [Step 3](#configure-iam). | +| `--aws-lambda-assume-role-external-id` | External ID configured in the IAM role trust policy. | @@ -349,8 +359,8 @@ temporal worker deployment create-version \ If you created the version through the Temporal UI, the version is already current and you can skip this step. -If you used the CLI, set the version as current. -Without this step, tasks on the Task Queue will not route to the version, and Temporal will not invoke the Lambda function. +If you used the CLI, set the version as current. Without this step, tasks on the Task Queue will not route to the +version, and Temporal will not invoke the Lambda function. ```bash temporal worker deployment set-current-version \ @@ -369,10 +379,11 @@ temporal workflow start \ --input '"Hello, serverless!"' ``` -When the task lands on the Task Queue with no active pollers, Temporal detects the compute provider configuration and invokes your Lambda function. -The Worker starts, connects to Temporal, picks up the task, and processes it. +When the task lands on the Task Queue with no active pollers, Temporal detects the compute provider configuration and +invokes your Lambda function. The Worker starts, connects to Temporal, picks up the task, and processes it. You can verify the invocation by checking: - **Temporal UI:** The Workflow execution should show task completions in the event history. -- **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. +- **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. diff --git a/docs/production-deployment/worker-deployments/serverless-workers/self-hosted-setup.mdx b/docs/production-deployment/worker-deployments/serverless-workers/self-hosted-setup.mdx new file mode 100644 index 0000000000..39ad4a041a --- /dev/null +++ b/docs/production-deployment/worker-deployments/serverless-workers/self-hosted-setup.mdx @@ -0,0 +1,210 @@ +--- +id: self-hosted-setup +title: Self-hosted setup for Serverless Workers +sidebar_label: Self-hosted setup +description: Configure a self-hosted Temporal Service to use Serverless Workers with AWS Lambda. +slug: /production-deployment/worker-deployments/serverless-workers/self-hosted-setup +toc_max_heading_level: 4 +keywords: + - serverless + - self-hosted + - lambda + - aws + - worker + - deployment +tags: + - Workers + - Deploy + - Serverless + - Self-hosted +--- + +This page covers the prerequisites for running [Serverless Workers](/serverless-workers) on a self-hosted Temporal +Service with AWS Lambda: + +1. Ensure Lambda can reach the Temporal Service. +2. Enable the Worker Controller Instance (WCI) on the server through dynamic configuration. +3. Provide the server with AWS credentials to assume IAM roles. +4. Create an IAM role in your AWS account that grants Temporal permission to invoke Lambda functions. + +Once setup is complete, follow the +[AWS Lambda deployment guide](/production-deployment/worker-deployments/serverless-workers/aws-lambda) to deploy your +Worker. + +## Ensure Lambda can reach the Temporal Service {#ensure-network-reachability} + +The [Temporal Service frontend](/temporal-service#frontend-service) must be reachable from the Lambda execution +environment. How to achieve this depends on your network setup. If the Temporal Service runs on a private network, you +may need [VPC access for Lambda](https://docs.aws.amazon.com/lambda/latest/dg/configuration-vpc.html), VPC peering, or a +similar mechanism to allow the Lambda function to connect to the Temporal frontend. + +## Enable the Worker Controller Instance {#enable-worker-controller} + +[WCI](/serverless-workers#how-invocation-works) is the server component that monitors Task Queues and invokes compute +providers. It is disabled by default and must be enabled through +[dynamic configuration](/references/dynamic-configuration). + +Add the following keys to your dynamic config file: + +```yaml +workercontroller.enabled: + - value: true + +workercontroller.compute_providers.enabled: + - value: + - aws-lambda + +workercontroller.scaling_algorithms.enabled: + - value: + - no-sync +``` + +To enable WCI for specific Namespaces instead of globally, add a `constraints` section with the +Namespace name under `workercontroller.enabled`. For example, to enable WCI only for `your-namespace`: + +```yaml +workercontroller.enabled: + - value: true + constraints: + namespace: 'your-namespace' +``` + +The Temporal Service watches the dynamic config file for changes and applies updates without a restart. + +## Configure AWS credentials {#configure-aws-credentials} + +The Temporal Service needs AWS credentials to assume an IAM role that invokes Lambda functions. How you provide +credentials depends on where the Temporal Service runs. + +**On AWS infrastructure (EC2, ECS, EKS):** The server uses the attached instance role, task role, or pod role +automatically. No additional credential configuration is needed. The attached role must have `sts:AssumeRole` permission +for the Lambda invocation role created in the next step. + +**Outside AWS:** Use [IAM Roles Anywhere](https://aws.amazon.com/iam/roles-anywhere/), or configure static AWS +credentials in the server's environment (not recommended): + +``` +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_REGION= +``` + +These credentials must belong to an IAM user or role that has `sts:AssumeRole` permission for the Lambda invocation +role. + +## Create the Lambda invocation role {#create-invocation-role} + +Temporal invokes Lambda functions by assuming an IAM role in your AWS account. This role needs `lambda:GetFunction` and +`lambda:InvokeFunction` permission on your Worker Lambda functions, and a trust policy that allows the Temporal server's +identity to assume it. + +Deploy the following CloudFormation template to create the role. +[Download the template](/files/temporal-self-hosted-serverless-worker-role.yaml). Replace the parameter values in the +command below and run it in your terminal: + +```bash +aws cloudformation create-stack --stack-name temporal-serverless-worker --template-body file://temporal-self-hosted-serverless-worker-role.yaml --parameters ParameterKey=TemporalIamRoleArn,ParameterValue= ParameterKey=AssumeRoleExternalId,ParameterValue= ParameterKey=LambdaFunctionARNs,ParameterValue='""' --capabilities CAPABILITY_NAMED_IAM --region +``` + +| Parameter | Description | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `TemporalIamRoleArn` | The ARN of the IAM role or user that the Temporal Service runs as. This is the identity the server uses to call `sts:AssumeRole`. To find the ARN, run `aws sts get-caller-identity` in the server's environment. | +| `AssumeRoleExternalId` | A unique string to prevent [confused deputy](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html) attacks. Choose any value and pass the same value when creating the Worker Deployment Version. | +| `LambdaFunctionARNs` | Comma-separated list of Lambda function ARNs that Temporal may invoke. | +| `RoleName` | Base name for the created IAM role. Defaults to `Temporal-Serverless-Worker`. | + +
+CloudFormation template + +```yaml +AWSTemplateFormatVersion: '2010-09-09' +Description: + Creates an IAM role that a self-hosted Temporal Service can assume to invoke Lambda functions for Serverless Workers. + +Parameters: + TemporalIamRoleArn: + Type: String + Description: The ARN of the IAM role or user that the Temporal Service runs as. + + AssumeRoleExternalId: + Type: String + Description: A unique identifier to prevent confused deputy attacks. + AllowedPattern: '[a-zA-Z0-9_+=,.@-]*' + MinLength: 5 + MaxLength: 45 + + LambdaFunctionARNs: + Type: CommaDelimitedList + Description: >- + Comma-separated list of Lambda function ARNs to invoke (e.g., + arn:aws:lambda:us-west-2:123456789012:function:worker-1,arn:aws:lambda:us-west-2:123456789012:function:worker-2) + + RoleName: + Type: String + Default: 'Temporal-Serverless-Worker' + +Resources: + TemporalServerlessWorker: + Type: AWS::IAM::Role + Properties: + RoleName: !Sub '${RoleName}-${AWS::StackName}' + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + AWS: [!Ref TemporalIamRoleArn] + Action: sts:AssumeRole + Condition: + StringEquals: + 'sts:ExternalId': [!Ref AssumeRoleExternalId] + Description: 'The role the Temporal Service uses to invoke Lambda functions for Serverless Workers' + MaxSessionDuration: 3600 + + TemporalLambdaInvokePermissions: + Type: AWS::IAM::Policy + DependsOn: TemporalServerlessWorker + Properties: + PolicyName: 'Temporal-Lambda-Invoke-Permissions' + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - lambda:InvokeFunction + - lambda:GetFunction + Resource: !Ref LambdaFunctionARNs + Roles: + - !Sub '${RoleName}-${AWS::StackName}' + +Outputs: + RoleARN: + Description: The ARN of the IAM role created for the Temporal Service + Value: !GetAtt TemporalServerlessWorker.Arn + Export: + Name: !Sub '${AWS::StackName}-RoleARN' + + RoleName: + Description: The name of the IAM role + Value: !Ref RoleName + + LambdaFunctionARNs: + Description: The Lambda function ARNs that can be invoked + Value: !Join [', ', !Ref LambdaFunctionARNs] +``` + +
+ +After the stack finishes creating, retrieve the IAM role ARN from the stack outputs: + +```bash +aws cloudformation describe-stacks --stack-name temporal-serverless-worker --query 'Stacks[0].Outputs[?OutputKey==`RoleARN`].OutputValue' --output text --region +``` + +Use this role ARN when creating the Worker Deployment Version. + +## Next steps {#next-steps} + +Follow the [AWS Lambda deployment guide](/production-deployment/worker-deployments/serverless-workers/aws-lambda) to +write your Worker code, deploy it to Lambda, and create a Worker Deployment Version with the IAM role from the previous +step. diff --git a/sidebars.js b/sidebars.js index 995dd27f9a..39fdc7a644 100644 --- a/sidebars.js +++ b/sidebars.js @@ -1219,6 +1219,7 @@ module.exports = { }, items: [ 'production-deployment/worker-deployments/serverless-workers/aws-lambda', + 'production-deployment/worker-deployments/serverless-workers/self-hosted-setup', ], }, ], diff --git a/static/files/temporal-self-hosted-serverless-worker-role.yaml b/static/files/temporal-self-hosted-serverless-worker-role.yaml new file mode 100644 index 0000000000..488fa1ad96 --- /dev/null +++ b/static/files/temporal-self-hosted-serverless-worker-role.yaml @@ -0,0 +1,74 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: Creates an IAM role that a self-hosted Temporal Service can assume to invoke Lambda functions for Serverless Workers. + +Parameters: + TemporalIamRoleArn: + Type: String + Description: The ARN of the IAM role or user that the Temporal Service runs as. + + AssumeRoleExternalId: + Type: String + Description: A unique identifier to prevent confused deputy attacks. + AllowedPattern: '[a-zA-Z0-9_+=,.@-]*' + MinLength: 5 + MaxLength: 45 + + LambdaFunctionARNs: + Type: CommaDelimitedList + Description: >- + Comma-separated list of Lambda function ARNs to invoke + (e.g., arn:aws:lambda:us-west-2:123456789012:function:worker-1,arn:aws:lambda:us-west-2:123456789012:function:worker-2) + + RoleName: + Type: String + Default: 'Temporal-Serverless-Worker' + +Resources: + TemporalServerlessWorker: + Type: AWS::IAM::Role + Properties: + RoleName: !Sub '${RoleName}-${AWS::StackName}' + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + AWS: + [!Ref TemporalIamRoleArn] + Action: sts:AssumeRole + Condition: + StringEquals: + 'sts:ExternalId': [!Ref AssumeRoleExternalId] + Description: "The role the Temporal Service uses to invoke Lambda functions for Serverless Workers" + MaxSessionDuration: 3600 + + TemporalLambdaInvokePermissions: + Type: AWS::IAM::Policy + DependsOn: TemporalServerlessWorker + Properties: + PolicyName: 'Temporal-Lambda-Invoke-Permissions' + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - lambda:InvokeFunction + - lambda:GetFunction + Resource: !Ref LambdaFunctionARNs + Roles: + - !Sub '${RoleName}-${AWS::StackName}' + +Outputs: + RoleARN: + Description: The ARN of the IAM role created for the Temporal Service + Value: !GetAtt TemporalServerlessWorker.Arn + Export: + Name: !Sub "${AWS::StackName}-RoleARN" + + RoleName: + Description: The name of the IAM role + Value: !Ref RoleName + + LambdaFunctionARNs: + Description: The Lambda function ARNs that can be invoked + Value: !Join [", ", !Ref LambdaFunctionARNs] From 4965335ecc581b91f08612d10ea5db25f8a4acc4 Mon Sep 17 00:00:00 2001 From: Lenny Chen <55669665+lennessyy@users.noreply.github.com> Date: Tue, 28 Apr 2026 11:38:18 -0700 Subject: [PATCH 20/27] feat: serverless worker - typescript (#4468) * feat: serverless worker - typescript * docs: fix TS serverless worker review items - Add HOME=/tmp to TS create-function env vars - Fix makeOtelPlugins() to makeOtelPlugin() to match SDK export - Add versioning behavior requirement note Co-Authored-By: Claude Opus 4.6 (1M context) * docs: fix ADOT layer instructions and add X-Ray permissions note - Correct two-layer instruction to single ADOT Node.js layer (includes collector) - Add AWS_LAMBDA_EXEC_WRAPPER env var requirement - Add AWSXRayDaemonWriteAccess policy requirement with silent failure warning Co-Authored-By: Claude Opus 4.6 (1M context) * docs: revert to two-layer ADOT setup, keep permissions note Revert to the original two-layer ADOT instruction from the SDK author. The single-layer setup was not verified end-to-end. Keep the AWSXRayDaemonWriteAccess and AWS_LAMBDA_EXEC_WRAPPER additions. Co-Authored-By: Claude Opus 4.6 (1M context) * docs: add custom OTel collector config and setup instructions for TS The default ADOT collector config does not route OTLP data to the traces pipeline. Add the custom collector config YAML that wires OTLP to both traces (X-Ray) and metrics (CloudWatch EMF). Document the required env vars and IAM permissions. Co-Authored-By: Claude Opus 4.6 (1M context) * docs: fix OTel collector config to use logging exporter The standalone ADOT collector layer (v0.40.0) does not support the debug exporter. Replace with logging, which is the supported equivalent. Co-Authored-By: Claude Opus 4.6 (1M context) * docs: update TS OTel to match sample (two layers, debug exporter) Match the TS sample exactly: - Two ADOT layers (JS layer + standalone collector) - debug exporter (not logging) - AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-instrument - OPENTELEMETRY_COLLECTOR_CONFIG_URI (not _FILE) - tracing-config Mode=Active - IAM permissions for xray, cloudwatch Co-Authored-By: Claude Opus 4.6 (1M context) * docs: fix AWS_LAMBDA_EXEC_WRAPPER to /opt/otel-handler for Node.js The Node.js ADOT layer ships /opt/otel-handler, not /opt/otel-instrument (which is the Python wrapper). Verified end-to-end: X-Ray traces confirmed. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- .../workers/serverless-workers/aws-lambda.mdx | 212 ++++++++++++++++++ .../workers/serverless-workers/index.mdx | 26 +++ .../serverless-workers/aws-lambda.mdx | 130 +++++++++-- sidebars.js | 12 + 4 files changed, 358 insertions(+), 22 deletions(-) create mode 100644 docs/develop/typescript/workers/serverless-workers/aws-lambda.mdx create mode 100644 docs/develop/typescript/workers/serverless-workers/index.mdx diff --git a/docs/develop/typescript/workers/serverless-workers/aws-lambda.mdx b/docs/develop/typescript/workers/serverless-workers/aws-lambda.mdx new file mode 100644 index 0000000000..cac36d59ba --- /dev/null +++ b/docs/develop/typescript/workers/serverless-workers/aws-lambda.mdx @@ -0,0 +1,212 @@ +--- +id: aws-lambda +title: Serverless Workers on AWS Lambda - TypeScript SDK +sidebar_label: Serverless Workers on AWS Lambda +description: Write a Temporal Worker that runs on AWS Lambda using the TypeScript SDK @temporalio/lambda-worker package. +slug: /develop/typescript/workers/serverless-workers/aws-lambda +toc_max_heading_level: 4 +keywords: + - serverless + - lambda + - aws + - typescript sdk + - worker + - serverless worker +tags: + - Workers + - TypeScript SDK + - Serverless + - AWS Lambda +--- + +The `@temporalio/lambda-worker` package lets you run a Temporal Serverless Worker on AWS Lambda. +Deploy your Worker code as a Lambda function, and Temporal Cloud invokes it when Tasks arrive. +Each invocation starts a Worker, polls for Tasks, then gracefully shuts down before a configurable invocation deadline. +You register Workflows and Activities the same way you would with a standard Worker. + +For a full end-to-end deployment guide covering AWS IAM setup, compute configuration, and verification, see [Deploy a Serverless Worker on AWS Lambda](/production-deployment/worker-deployments/serverless-workers/aws-lambda). + +## Create and run a Worker in Lambda {#create-and-run} + +Use the `runWorker` function to create a Lambda handler that runs a Temporal Worker. +Pass a deployment version and a configure callback that sets up your Workflows and Activities. + +```typescript +import { runWorker } from '@temporalio/lambda-worker'; +import * as activities from './activities'; + +export const handler = runWorker( + { deploymentName: 'my-app', buildId: 'build-1' }, + (config) => { + config.workerOptions.taskQueue = 'my-task-queue'; + config.workerOptions.workflowBundle = { + codePath: require.resolve('./workflow-bundle.js'), + }; + config.workerOptions.activities = activities; + }, +); +``` + +The deployment version is required. +Worker Deployment Versioning is always enabled for Serverless Workers. +Each Workflow must declare a [versioning behavior](/worker-versioning#versioning-behaviors), either `AutoUpgrade` or `Pinned`. +The default versioning behavior is `PINNED`. To change it, set `workerDeploymentOptions.defaultVersioningBehavior` in the configure callback. + +### Pre-bundle Workflow code {#pre-bundle} + +Use `workflowBundle` with pre-bundled code instead of `workflowsPath`. +Pre-bundling avoids webpack bundling overhead on every Lambda cold start. + +Build the bundle as a separate build step: + +```typescript +import { bundleWorkflowCode } from '@temporalio/worker'; +import { writeFile } from 'fs/promises'; + +const { code } = await bundleWorkflowCode({ + workflowsPath: require.resolve('./workflows'), +}); +await writeFile('./workflow-bundle.js', code); +``` + +Then reference the bundle in your handler with `workflowBundle: { codePath: require.resolve('./workflow-bundle.js') }`. + +## Configure the Temporal connection {#configure-connection} + +The `@temporalio/lambda-worker` package automatically loads Temporal client configuration from a TOML config file and environment variables. Refer to [Environment Configuration](/develop/environment-configuration) for more details. + +The config file is resolved in order: + +1. `TEMPORAL_CONFIG_FILE` environment variable, if set. +2. `temporal.toml` in `$LAMBDA_TASK_ROOT` (typically `/var/task`). +3. `temporal.toml` in the current working directory. + +The file is optional. If absent, only environment variables are used. + +Encrypt sensitive values like TLS keys or API keys. Refer to [AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars-encryption.html) for options. + +## Adjust Worker defaults for Lambda {#lambda-tuned-defaults} + +The `@temporalio/lambda-worker` package applies conservative defaults suited to short-lived Lambda invocations. +These differ from standard Worker defaults to avoid overcommitting resources in a constrained environment. + +| Setting | Lambda default | +|---|---| +| `maxConcurrentActivityTaskExecutions` | 2 | +| `maxConcurrentWorkflowTaskExecutions` | 10 | +| `maxConcurrentLocalActivityExecutions` | 2 | +| `maxConcurrentNexusTaskExecutions` | 5 | +| `workflowTaskPollerBehavior` | `SimpleMaximum(2)` | +| `activityTaskPollerBehavior` | `SimpleMaximum(1)` | +| `nexusTaskPollerBehavior` | `SimpleMaximum(1)` | +| `shutdownGraceTime` | 5 seconds | +| `maxCachedWorkflows` | 30 | +| `shutdownDeadlineBufferMs` | 7000 | + +Eager Activities are not supported. Lambda invocations don't maintain persistent connections. + +`shutdownDeadlineBufferMs` is specific to the `@temporalio/lambda-worker` package. +It controls how much time before the Lambda deadline the Worker begins its graceful shutdown. +The default is `shutdownGraceTime` (5s) + 2s. + +If your Worker handles long-running Activities, increase `shutdownGraceTime`, `shutdownDeadlineBufferMs`, and the Lambda invocation deadline (`--timeout`) together. +For guidance on how these values relate, see [Tuning for long-running Activities](/serverless-workers#tuning-for-long-running-activities). + +## Add observability with OpenTelemetry {#add-observability} + +The `@temporalio/lambda-worker/otel` module provides OpenTelemetry integration with defaults configured for the [AWS Distro for OpenTelemetry (ADOT)](https://aws-otel.github.io/docs/getting-started/lambda) Lambda layers. +With this enabled, the Worker emits SDK metrics and distributed traces for Workflow and Activity executions. + +The underlying metrics and traces are the same ones the TypeScript SDK emits in any environment. +For general observability concepts and the full list of available metrics, see [Observability - TypeScript SDK](/develop/typescript/observability) and the [SDK metrics reference](/references/sdk-metrics). + +```typescript +import { runWorker } from '@temporalio/lambda-worker'; +import { applyDefaults } from '@temporalio/lambda-worker/otel'; +import * as activities from './activities'; + +export const handler = runWorker( + { deploymentName: 'my-app', buildId: 'build-1' }, + (config) => { + config.workerOptions.taskQueue = 'my-task-queue'; + config.workerOptions.workflowBundle = { + codePath: require.resolve('./workflow-bundle.js'), + }; + config.workerOptions.activities = activities; + applyDefaults(config); + }, +); +``` + +`applyDefaults` registers Temporal SDK interceptors for tracing and configures the Core SDK to export metrics via OTLP. +By default, telemetry is sent to `localhost:4317`, which is the ADOT Lambda layer's default collector endpoint. + +To collect this telemetry, attach two ADOT Lambda layers: + +1. The [ADOT JavaScript layer](https://aws-otel.github.io/docs/getting-started/lambda/lambda-js) for Node.js-side auto-instrumentation and trace export. +2. The [ADOT Collector layer](https://aws-otel.github.io/docs/getting-started/lambda) (`aws-otel-collector-amd64`) to run the OTel Collector as a Lambda extension, receiving telemetry via OTLP on `localhost:4317` and forwarding traces to X-Ray and metrics to CloudWatch. + +The default Collector configuration does not route OTLP data to the traces pipeline. +You must provide a custom Collector configuration that wires the OTLP receiver to both the traces and metrics pipelines. +Bundle the following `otel-collector-config.yaml` in your Lambda deployment package: + +```yaml +receivers: + otlp: + protocols: + grpc: + endpoint: 'localhost:4317' + http: + endpoint: 'localhost:4318' + +exporters: + debug: + awsxray: + region: + awsemf: + namespace: TemporalWorkerMetrics + log_group_name: /aws/lambda/ + region: + dimension_rollup_option: NoDimensionRollup + resource_to_telemetry_conversion: + enabled: true + +service: + pipelines: + traces: + receivers: [otlp] + exporters: [awsxray, debug] + metrics: + receivers: [otlp] + exporters: [awsemf] +``` + +Set the following environment variables on the Lambda function: + +- `AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-handler` +- `OPENTELEMETRY_COLLECTOR_CONFIG_URI=/var/task/otel-collector-config.yaml` + +Enable X-Ray active tracing on the Lambda function: + +```bash +aws lambda update-function-configuration \ + --function-name \ + --tracing-config Mode=Active +``` + +The Lambda execution role must have permissions to write to X-Ray and CloudWatch. +Add `xray:PutTraceSegments`, `xray:PutTelemetryRecords`, and `cloudwatch:PutMetricData` permissions to the execution role. +Without these permissions, the Collector fails silently and no telemetry appears. + +When pre-bundling Workflow code, pass the plugin from `makeOtelPlugin()` so that Workflow interceptor modules are included in the bundle: + +```typescript +import { bundleWorkflowCode } from '@temporalio/worker'; +import { makeOtelPlugin } from '@temporalio/lambda-worker/otel'; + +const { plugin } = makeOtelPlugin(); +const { code } = await bundleWorkflowCode({ + workflowsPath: require.resolve('./workflows'), + plugins: [plugin], +}); +``` diff --git a/docs/develop/typescript/workers/serverless-workers/index.mdx b/docs/develop/typescript/workers/serverless-workers/index.mdx new file mode 100644 index 0000000000..58f3e71046 --- /dev/null +++ b/docs/develop/typescript/workers/serverless-workers/index.mdx @@ -0,0 +1,26 @@ +--- +id: index +title: Serverless Workers - TypeScript SDK +sidebar_label: Serverless Workers +description: Write Temporal Workers that run on serverless compute using the TypeScript SDK. +slug: /develop/typescript/workers/serverless-workers +toc_max_heading_level: 4 +keywords: + - serverless + - typescript sdk + - worker +tags: + - Workers + - TypeScript SDK + - Serverless +--- + +Serverless Workers run on ephemeral, on-demand compute rather than long-lived processes. +Temporal invokes the Worker when Tasks arrive, and the Worker shuts down when the work is done. + +For a general overview of how Serverless Workers work, see [Serverless Workers](/serverless-workers). +For the end-to-end deployment guide, see [Deploy a Serverless Worker](/production-deployment/worker-deployments/serverless-workers). + +## Supported providers + +- [**AWS Lambda**](/develop/typescript/workers/serverless-workers/aws-lambda) - Use the `@temporalio/lambda-worker` package to run a Worker as a Lambda function. Covers setup, configuration, Lambda-tuned defaults, and observability. diff --git a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx index 158ca2bf07..f1d6d71442 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx @@ -40,6 +40,11 @@ This guide walks through deploying a Temporal [Serverless Worker](/serverless-wo - The [Go SDK](/develop/go) (`go.temporal.io/sdk`)
+ + +- The [TypeScript SDK](/develop/typescript) (`@temporalio/lambda-worker`) + +
## 1. Write Worker code {#write-worker-code} @@ -85,6 +90,31 @@ For details on configuration options, Lambda-tuned defaults, and the invocation [Serverless Workers - Go SDK](/develop/go/workers/serverless-workers/aws-lambda). + + +Use the `@temporalio/lambda-worker` package. + +```typescript +import { runWorker } from '@temporalio/lambda-worker'; +import * as activities from './activities'; + +export const handler = runWorker( + { deploymentName: 'my-app', buildId: 'build-1' }, + (config) => { + config.workerOptions.taskQueue = 'my-task-queue'; + config.workerOptions.workflowBundle = { + codePath: require.resolve('./workflow-bundle.js'), + }; + config.workerOptions.activities = activities; + }, +); +``` + +Use `workflowBundle` with pre-bundled code instead of `workflowsPath` to avoid webpack bundling overhead on Lambda cold starts. + +For details on configuration options, Lambda-tuned defaults, and observability, see [Serverless Workers - TypeScript SDK](/develop/typescript/workers/serverless-workers/aws-lambda). + + ## 2. Deploy Lambda function {#deploy-lambda-function} @@ -109,10 +139,30 @@ zip function.zip bootstrap ``` + + +Build the Workflow bundle and compile the project: + +```bash +npx ts-node src/scripts/build-workflow-bundle.ts +npx tsc +``` + +Install production dependencies and package everything into a zip: + +```bash +npm install --omit=dev +zip -r function.zip lib/ node_modules/ workflow-bundle.js +``` + + ### ii. Deploy Lambda function {#deploy-lambda-function-step} + + + ```bash aws lambda create-function \ --function-name my-temporal-worker \ @@ -125,28 +175,54 @@ aws lambda create-function \ --environment "Variables={HOME=/tmp,TEMPORAL_ADDRESS=:7233,TEMPORAL_NAMESPACE=,TEMPORAL_API_KEY=}" ``` -| Parameter | Description | -| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `--function-name` | Name of the Lambda function. | -| `--runtime` | Lambda runtime. Use `provided.al2023` for custom Go binaries. | -| `--handler` | Entry point binary name. Must be `bootstrap` when using the `provided.al2023` custom runtime. | -| `--role` | ARN of the Lambda [execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html), which grants the function permission to run. Trusted principal must be `lambda.amazonaws.com`. This is separate from the role Temporal uses to invoke the function in [Step 3](#configure-iam). The role must have at least the [`AWSLambdaBasicExecutionRole`](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaBasicExecutionRole.html) managed policy attached. | -| `--zip-file` | Path to your packaged deployment zip. | -| `--timeout` | Invocation deadline in seconds. This is the maximum time each Lambda invocation can run before AWS terminates it. Set this high enough for the Worker to start, process Tasks, and [shut down gracefully](/serverless-workers#worker-lifecycle). | -| `--memory-size` | Memory in MB allocated to each invocation. | -| `HOME` | Must be `/tmp`. | -| `TEMPORAL_ADDRESS` | Temporal frontend address (e.g., `..tmprl.cloud:7233`). | -| `TEMPORAL_NAMESPACE` | Temporal Namespace. | -| `TEMPORAL_TASK_QUEUE` | Task Queue name. Overrides the value set in code. | -| `TEMPORAL_TLS_CLIENT_CERT_PATH` | Path to the TLS client certificate file for mTLS authentication. | -| `TEMPORAL_TLS_CLIENT_KEY_PATH` | Path to the TLS client key file for mTLS authentication. | -| `TEMPORAL_API_KEY` | API key for API key authentication. | - -The `lambdaworker` package reads environment variables automatically at startup. For the full list, see -[Client environment configuration](/references/client-environment-configuration). - -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. +| Parameter | Description | +|---|---| +| `--function-name` | Name of the Lambda function. | +| `--runtime` | Lambda runtime. Use `provided.al2023` for custom Go binaries. | +| `--handler` | Entry point binary name. Must be `bootstrap` when using the `provided.al2023` custom runtime. | + + + + +```bash +aws lambda create-function \ + --function-name my-temporal-worker \ + --runtime nodejs22.x \ + --handler lib/index.handler \ + --role arn:aws:iam:::role/my-temporal-worker-execution \ + --zip-file fileb://function.zip \ + --timeout 600 \ + --memory-size 256 \ + --environment "Variables={HOME=/tmp,TEMPORAL_ADDRESS=:7233,TEMPORAL_NAMESPACE=,TEMPORAL_API_KEY=}" +``` + +| Parameter | Description | +|---|---| +| `--function-name` | Name of the Lambda function. | +| `--runtime` | Lambda runtime. Use `nodejs22.x` or another supported Node.js version (20+). | +| `--handler` | Entry point in `module.export` format. Must point to the handler exported by `runWorker`. | + + + + +The following parameters apply to all SDKs: + +| Parameter | Description | +|---|---| +| `--role` | ARN of the Lambda [execution role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html), which grants the function permission to run. Trusted principal must be `lambda.amazonaws.com`. This is separate from the role Temporal uses to invoke the function in [Step 3](#configure-iam). The role must have at least the [`AWSLambdaBasicExecutionRole`](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSLambdaBasicExecutionRole.html) managed policy attached. | +| `--zip-file` | Path to your packaged deployment zip. | +| `--timeout` | Invocation deadline in seconds. This is the maximum time each Lambda invocation can run before AWS terminates it. Set this high enough for the Worker to start, process Tasks, and [shut down gracefully](/serverless-workers#worker-lifecycle). | +| `--memory-size` | Memory in MB allocated to each invocation. | +| `TEMPORAL_ADDRESS` | Temporal frontend address (e.g., `..tmprl.cloud:7233`). | +| `TEMPORAL_NAMESPACE` | Temporal Namespace. | +| `TEMPORAL_TASK_QUEUE` | Task Queue name. Overrides the value set in code. | +| `TEMPORAL_TLS_CLIENT_CERT_PATH` | Path to the TLS client certificate file for mTLS authentication. | +| `TEMPORAL_TLS_CLIENT_KEY_PATH` | Path to the TLS client key file for mTLS authentication. | +| `TEMPORAL_API_KEY` | API key for API key authentication. | + +The serverless Worker packages read environment variables automatically at startup. For the full list, see [Client environment configuration](/references/client-environment-configuration). + +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. To update an existing function with new code: @@ -334,6 +410,16 @@ When you create a version through the UI, the version is automatically set as cu 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. +First, create the Worker Deployment if it does not already exist: + +```bash +temporal worker deployment create \ + --namespace \ + --name my-app +``` + +Then create the version with the compute provider configuration: + ```bash temporal worker deployment create-version \ --namespace \ diff --git a/sidebars.js b/sidebars.js index 39fdc7a644..bfdc4e2ef5 100644 --- a/sidebars.js +++ b/sidebars.js @@ -664,6 +664,18 @@ module.exports = { items: [ 'develop/typescript/workers/run-worker-process', 'develop/typescript/workers/interceptors', + { + type: 'category', + label: 'Serverless Workers', + collapsed: true, + link: { + type: 'doc', + id: 'develop/typescript/workers/serverless-workers/index', + }, + items: [ + 'develop/typescript/workers/serverless-workers/aws-lambda', + ], + }, ], }, { From 92ff7c18d0be8cae79f8d2d5f087d4c0702f7fad Mon Sep 17 00:00:00 2001 From: Lenny Chen <55669665+lennessyy@users.noreply.github.com> Date: Tue, 28 Apr 2026 11:57:46 -0700 Subject: [PATCH 21/27] feat: python serverless worker (#4467) * feat: python serverless worker * docs: add versioning behavior samples, packaging fixes, heading cleanup - Add versioning behavior (PINNED) to Python and Go code samples - Add VersioningBehavior import from temporalio.common for Python - Fix Python packaging to zip deps first then add app files - Change Go samples from AutoUpgrade to Pinned - Add Python and TypeScript tabs to deploy guide - Clean up deploy guide headings (remove articles) - Add Serverless Worker link in deploy guide intro Co-Authored-By: Claude Opus 4.6 (1M context) * Revert "docs: add versioning behavior samples, packaging fixes, heading cleanup" This reverts commit 6fe5cc5ed7e7bd047a20a555e83ebab8c6d5c7c7. * docs: add Python versioning behavior samples and packaging fix - Add versioning behavior (PINNED) example to Python SDK page and deploy guide - Fix Python packaging: use --platform manylinux2014_x86_64 for Lambda - Zip deps first, then add app files separately Co-Authored-By: Claude Opus 4.6 (1M context) * docs: restructure Python SDK page for readability Highlight run_worker lines, explain configure callback right after the code block, move versioning behavior below. Co-Authored-By: Claude Opus 4.6 (1M context) * docs: explain deployment name and build ID with links to versioning Co-Authored-By: Claude Opus 4.6 (1M context) * Apply suggestions from code review Co-authored-by: Lenny Chen <55669665+lennessyy@users.noreply.github.com> * docs: add custom OTel collector config and setup instructions for Python The default ADOT collector config does not route OTLP data to the traces pipeline. Add the custom collector config YAML that wires OTLP to both traces (X-Ray) and metrics (CloudWatch EMF). Document the required env var and IAM permissions. Co-Authored-By: Claude Opus 4.6 (1M context) * docs: fix OTel collector config to use logging exporter The standalone ADOT collector layer (v0.40.0) does not support the debug exporter. Replace with logging, which is the supported equivalent. Verified end-to-end: traces now appear in X-Ray. Co-Authored-By: Claude Opus 4.6 (1M context) * docs: update Python OTel to use language-specific ADOT layer Use the ADOT Python layer (includes auto-instrumentation + collector) instead of standalone collector layer. Matches the verified sample setup. Use debug exporter (supported by the newer collector in the language layer). Add AWS_LAMBDA_EXEC_WRAPPER and tracing-config Mode=Active. Co-Authored-By: Claude Opus 4.6 (1M context) * docs: fix bad merge --------- Co-authored-by: Claude Opus 4.6 (1M context) --- .../workers/serverless-workers/aws-lambda.mdx | 213 ++++++++++++++++++ .../workers/serverless-workers/index.mdx | 26 +++ .../serverless-workers/aws-lambda.mdx | 87 +++++++ sidebars.js | 12 + 4 files changed, 338 insertions(+) create mode 100644 docs/develop/python/workers/serverless-workers/aws-lambda.mdx create mode 100644 docs/develop/python/workers/serverless-workers/index.mdx diff --git a/docs/develop/python/workers/serverless-workers/aws-lambda.mdx b/docs/develop/python/workers/serverless-workers/aws-lambda.mdx new file mode 100644 index 0000000000..93ceb66843 --- /dev/null +++ b/docs/develop/python/workers/serverless-workers/aws-lambda.mdx @@ -0,0 +1,213 @@ +--- +id: aws-lambda +title: Serverless Workers on AWS Lambda - Python SDK +sidebar_label: Serverless Workers on AWS Lambda +description: Write a Temporal Worker that runs on AWS Lambda using the Python SDK lambda_worker package. +slug: /develop/python/workers/serverless-workers/aws-lambda +toc_max_heading_level: 4 +keywords: + - serverless + - lambda + - aws + - python sdk + - worker + - serverless worker +tags: + - Workers + - Python SDK + - Serverless + - AWS Lambda +--- + +The `lambda_worker` contrib package lets you run a Temporal Serverless Worker on AWS Lambda. +Deploy your Worker code as a Lambda function, and Temporal Cloud invokes it when Tasks arrive. +Each invocation starts a Worker, polls for Tasks, then gracefully shuts down before a configurable invocation deadline. +You register Workflows and Activities the same way you would with a standard Worker. + +For a full end-to-end deployment guide covering AWS IAM setup, compute configuration, and verification, see [Deploy a Serverless Worker](/production-deployment/worker-deployments/serverless-workers). + +## Create and run a Worker in Lambda {#create-and-run} + +Use the `run_worker` function to create a Lambda handler that runs a Temporal Worker. +Pass a `WorkerDeploymentVersion` and a configure callback that registers your Workflows and Activities. + +```python {14-20} +from temporalio.common import WorkerDeploymentVersion +from temporalio.contrib.aws.lambda_worker import LambdaWorkerConfig, run_worker + +from my_workflows import MyWorkflow +from my_activities import my_activity + + +def configure(config: LambdaWorkerConfig) -> None: + config.worker_config["task_queue"] = "my-task-queue" + config.worker_config["workflows"] = [MyWorkflow] + config.worker_config["activities"] = [my_activity] + + +lambda_handler = run_worker( + WorkerDeploymentVersion( + deployment_name="my-app", + build_id="build-1", + ), + configure, +) +``` + +`run_worker` takes a `WorkerDeploymentVersion` and a configure callback, and returns a Lambda handler. +The `WorkerDeploymentVersion` identifies the [Worker Deployment](/worker-versioning#worker-deployments) and [Build ID](/worker-versioning#build-id) for this Worker. +The deployment name groups related Workers across versions, and the Build ID identifies a specific release of your Worker code. +Worker Versioning is required for Serverless Workers. + +The `configure` callback receives a `LambdaWorkerConfig` dataclass with fields pre-populated with Lambda-appropriate defaults. +Set the Task Queue, Workflows, and Activities through `worker_config`, which accepts the same keyword arguments as the `Worker` constructor. + +Each Workflow registered with the Worker must declare a [versioning behavior](/worker-versioning#versioning-behaviors) in the `@workflow.defn` decorator, either `PINNED` or `AUTO_UPGRADE`: + +```python {5} +from temporalio import workflow +from temporalio.common import VersioningBehavior + + +@workflow.defn(versioning_behavior=VersioningBehavior.PINNED) +class MyWorkflow: + @workflow.run + async def run(self, input: str) -> str: + ... +``` + +## Configure the Temporal connection {#configure-connection} + +The `lambda_worker` package automatically loads Temporal client configuration from a TOML config file and environment variables. Refer to [Environment Configuration](/develop/environment-configuration) for more details. + +The config file is resolved in order: + +1. `TEMPORAL_CONFIG_FILE` environment variable, if set. +2. `temporal.toml` in `$LAMBDA_TASK_ROOT` (typically `/var/task`). +3. `temporal.toml` in the current working directory. + +The file is optional. If absent, only environment variables are used. + +Encrypt sensitive values like TLS keys or API keys. Refer to [AWS documentation](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars-encryption.html) for options. + +## Adjust Worker defaults for Lambda {#lambda-tuned-defaults} + +The `lambda_worker` package applies conservative defaults suited to short-lived Lambda invocations. +These differ from standard Worker defaults to avoid overcommitting resources in a constrained environment. + +| Setting | Lambda default | +|---|---| +| `max_concurrent_activities` | 2 | +| `max_concurrent_workflow_tasks` | 10 | +| `max_concurrent_local_activities` | 2 | +| `max_concurrent_nexus_tasks` | 5 | +| `workflow_task_poller_behavior` | `SimpleMaximum(2)` | +| `activity_task_poller_behavior` | `SimpleMaximum(1)` | +| `nexus_task_poller_behavior` | `SimpleMaximum(1)` | +| `graceful_shutdown_timeout` | 5 seconds | +| `max_cached_workflows` | 30 | +| `disable_eager_activity_execution` | Always `True` | +| `shutdown_deadline_buffer` | 7 seconds | + +`disable_eager_activity_execution` is always `True` and cannot be overridden. +Eager Activities require a persistent connection, which Lambda invocations don't maintain. + +`shutdown_deadline_buffer` is specific to the `lambda_worker` package. +It controls how much time before the Lambda deadline the Worker begins its graceful shutdown. +The default is `graceful_shutdown_timeout` + 2 seconds. + +If your Worker handles long-running Activities, increase `graceful_shutdown_timeout`, `shutdown_deadline_buffer`, and the Lambda invocation deadline (`--timeout`) together. +For guidance on how these values relate, see [Tuning for long-running Activities](/serverless-workers#tuning-for-long-running-activities). + +## Add observability with OpenTelemetry {#add-observability} + +The `lambda_worker.otel` module provides OpenTelemetry integration with defaults configured for the [AWS Distro for OpenTelemetry (ADOT)](https://aws-otel.github.io/docs/getting-started/lambda) Lambda layer. +With this enabled, the Worker emits SDK metrics and distributed traces for Workflow and Activity executions. +The ADOT Lambda layer collects this telemetry and can forward traces to AWS X-Ray and metrics to Amazon CloudWatch. + +The underlying metrics and traces are the same ones the Python SDK emits in any environment. +For general observability concepts and the full list of available metrics, see [Observability - Python SDK](/develop/python/observability) and the [SDK metrics reference](/references/sdk-metrics). + +from temporalio.common import WorkerDeploymentVersion +from temporalio.contrib.aws.lambda_worker import LambdaWorkerConfig, run_worker +from temporalio.contrib.aws.lambda_worker.otel import apply_defaults + +from my_workflows import MyWorkflow +from my_activities import my_activity + + +def configure(config: LambdaWorkerConfig) -> None: + config.worker_config["task_queue"] = "my-task-queue" + config.worker_config["workflows"] = [MyWorkflow] + config.worker_config["activities"] = [my_activity] + apply_defaults(config) + + +lambda_handler = run_worker( + WorkerDeploymentVersion( + deployment_name="my-app", + build_id="build-1", + ), + configure, +) +``` + +`apply_defaults` configures both metrics and tracing. +By default, telemetry is sent to `localhost:4317`, which is the ADOT Lambda layer's default collector endpoint. + +To collect this telemetry, attach the [ADOT Python Lambda layer](https://aws-otel.github.io/docs/getting-started/lambda/lambda-python) to your Lambda function. +The layer includes both auto-instrumentation and an OpenTelemetry Collector that receives telemetry on `localhost:4317` and forwards traces to AWS X-Ray and metrics to Amazon CloudWatch. + +The default Collector configuration does not route OTLP data to the traces pipeline. +You must provide a custom Collector configuration that wires the OTLP receiver to both the traces and metrics pipelines. +Bundle the following `otel-collector-config.yaml` in your Lambda deployment package: + +```yaml +receivers: + otlp: + protocols: + grpc: + endpoint: 'localhost:4317' + http: + endpoint: 'localhost:4318' + +exporters: + debug: + awsxray: + region: + awsemf: + namespace: TemporalWorkerMetrics + log_group_name: /aws/lambda/ + region: + dimension_rollup_option: NoDimensionRollup + resource_to_telemetry_conversion: + enabled: true + +service: + pipelines: + traces: + receivers: [otlp] + exporters: [awsxray, debug] + metrics: + receivers: [otlp] + exporters: [awsemf] +``` + +Set the following environment variables on the Lambda function: + +- `AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-instrument` +- `OPENTELEMETRY_COLLECTOR_CONFIG_FILE=/var/task/otel-collector-config.yaml` + +Enable X-Ray active tracing on the Lambda function: + +```bash +aws lambda update-function-configuration \ + --function-name \ + --tracing-config Mode=Active +``` + +The Lambda execution role must have permissions to write to X-Ray and CloudWatch. +Attach the [`AWSXRayDaemonWriteAccess`](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSXRayDaemonWriteAccess.html) managed policy, or add `xray:PutTraceSegments`, `xray:PutTelemetryRecords`, and `cloudwatch:PutMetricData` permissions. +Without these permissions, the Collector fails silently and no telemetry appears. + +If you only need metrics or tracing, use `build_metrics_telemetry_config` or `apply_tracing` individually. diff --git a/docs/develop/python/workers/serverless-workers/index.mdx b/docs/develop/python/workers/serverless-workers/index.mdx new file mode 100644 index 0000000000..eaa30b5c12 --- /dev/null +++ b/docs/develop/python/workers/serverless-workers/index.mdx @@ -0,0 +1,26 @@ +--- +id: index +title: Serverless Workers - Python SDK +sidebar_label: Serverless Workers +description: Write Temporal Workers that run on serverless compute using the Python SDK. +slug: /develop/python/workers/serverless-workers +toc_max_heading_level: 4 +keywords: + - serverless + - python sdk + - worker +tags: + - Workers + - Python SDK + - Serverless +--- + +Serverless Workers run on ephemeral, on-demand compute rather than long-lived processes. +Temporal invokes the Worker when Tasks arrive, and the Worker shuts down when the work is done. + +For a general overview of how Serverless Workers work, see [Serverless Workers](/serverless-workers). +For the end-to-end deployment guide, see [Deploy a Serverless Worker](/production-deployment/worker-deployments/serverless-workers). + +## Supported providers + +- [**AWS Lambda**](/develop/python/workers/serverless-workers/aws-lambda) - Use the `lambda_worker` contrib package to run a Worker as a Lambda function. Covers setup, configuration, Lambda-tuned defaults, and observability. diff --git a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx index f1d6d71442..a2442f65a4 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx @@ -40,6 +40,11 @@ This guide walks through deploying a Temporal [Serverless Worker](/serverless-wo - The [Go SDK](/develop/go) (`go.temporal.io/sdk`) + + +- The [Python SDK](/develop/python) (`temporalio`) + + - The [TypeScript SDK](/develop/typescript) (`@temporalio/lambda-worker`) @@ -90,6 +95,50 @@ For details on configuration options, Lambda-tuned defaults, and the invocation [Serverless Workers - Go SDK](/develop/go/workers/serverless-workers/aws-lambda). + + +Use the Python SDK's `lambda_worker` contrib package. + +```python +from temporalio.common import WorkerDeploymentVersion +from temporalio.contrib.aws.lambda_worker import LambdaWorkerConfig, run_worker + +from my_workflows import MyWorkflow +from my_activities import my_activity + + +def configure(config: LambdaWorkerConfig) -> None: + config.worker_config["task_queue"] = "my-task-queue" + config.worker_config["workflows"] = [MyWorkflow] + config.worker_config["activities"] = [my_activity] + + +lambda_handler = run_worker( + WorkerDeploymentVersion( + deployment_name="my-app", + build_id="build-1", + ), + configure, +) +``` + +Each Workflow must declare a [versioning behavior](/worker-versioning#versioning-behaviors) in the `@workflow.defn` decorator, either `PINNED` or `AUTO_UPGRADE`: + +```python +from temporalio import workflow +from temporalio.common import VersioningBehavior + + +@workflow.defn(versioning_behavior=VersioningBehavior.PINNED) +class MyWorkflow: + @workflow.run + async def run(self, input: str) -> str: + ... +``` + +For details on configuration options, Lambda-tuned defaults, and observability, see [Serverless Workers - Python SDK](/develop/python/workers/serverless-workers/aws-lambda). + + Use the `@temporalio/lambda-worker` package. @@ -139,6 +188,22 @@ zip function.zip bootstrap ``` + + +Install dependencies into a local directory for packaging. Use `--platform` to fetch Linux-compatible binaries for the Lambda runtime: + +```bash +pip install --target ./package --platform manylinux2014_x86_64 --only-binary=:all: temporalio +``` + +Package the dependencies and your application code into a zip file: + +```bash +cd package && zip -r ../function.zip . && cd .. +zip function.zip lambda_function.py my_workflows.py my_activities.py +``` + + Build the Workflow bundle and compile the project: @@ -182,6 +247,27 @@ aws lambda create-function \ | `--handler` | Entry point binary name. Must be `bootstrap` when using the `provided.al2023` custom runtime. | + + +```bash +aws lambda create-function \ + --function-name my-temporal-worker \ + --runtime python3.13 \ + --handler lambda_function.lambda_handler \ + --role arn:aws:iam:::role/my-temporal-worker-execution \ + --zip-file fileb://function.zip \ + --timeout 600 \ + --memory-size 256 \ + --environment "Variables={TEMPORAL_ADDRESS=:7233,TEMPORAL_NAMESPACE=,TEMPORAL_API_KEY=}" +``` + +| Parameter | Description | +|---|---| +| `--function-name` | Name of the Lambda function. | +| `--runtime` | Lambda runtime. Use `python3.13` or another supported Python version. | +| `--handler` | Entry point in `module.function` format. Must point to the handler returned by `run_worker`. | + + ```bash @@ -220,6 +306,7 @@ The following parameters apply to all SDKs: | `TEMPORAL_TLS_CLIENT_KEY_PATH` | Path to the TLS client key file for mTLS authentication. | | `TEMPORAL_API_KEY` | API key for API key authentication. | +The SDKs read environment variables automatically at startup. For the full list, see [Client environment configuration](/references/client-environment-configuration). The serverless Worker packages read environment variables automatically at startup. For the full list, see [Client environment configuration](/references/client-environment-configuration). 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. diff --git a/sidebars.js b/sidebars.js index bfdc4e2ef5..523e9049ca 100644 --- a/sidebars.js +++ b/sidebars.js @@ -524,6 +524,18 @@ module.exports = { items: [ 'develop/python/workers/run-worker-process', 'develop/python/workers/interceptors', + { + type: 'category', + label: 'Serverless Workers', + collapsed: true, + link: { + type: 'doc', + id: 'develop/python/workers/serverless-workers/index', + }, + items: [ + 'develop/python/workers/serverless-workers/aws-lambda', + ], + }, ], }, { From 11fc3701770edc55ff642462d538543688de481b Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Tue, 28 Apr 2026 13:39:59 -0700 Subject: [PATCH 22/27] copyedits --- .../worker-deployments/serverless-workers/aws-lambda.mdx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx index a2442f65a4..dcf382041d 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx @@ -30,6 +30,7 @@ This guide walks through deploying a Temporal [Serverless Worker](/serverless-wo - For self-hosted deployments, complete the [self-hosted setup](/production-deployment/worker-deployments/serverless-workers/self-hosted-setup) before following this guide. +- Every Workflow must declare a [versioning behavior](/worker-versioning#versioning-behaviors). - An AWS account with permissions to create and invoke Lambda functions and create IAM roles. - 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. @@ -155,12 +156,16 @@ export const handler = runWorker( codePath: require.resolve('./workflow-bundle.js'), }; config.workerOptions.activities = activities; + config.workerOptions.workerDeploymentOptions!.defaultVersioningBehavior = 'AUTO_UPGRADE'; }, ); ``` Use `workflowBundle` with pre-bundled code instead of `workflowsPath` to avoid webpack bundling overhead on Lambda cold starts. +Each Workflow must declare a [versioning behavior](/worker-versioning#versioning-behaviors), either `AUTO_UPGRADE` or `PINNED`. +Set it per-Workflow with `setWorkflowOptions` in the Workflow file, or set a default for all Workflows with `defaultVersioningBehavior` in the configure callback. + For details on configuration options, Lambda-tuned defaults, and observability, see [Serverless Workers - TypeScript SDK](/develop/typescript/workers/serverless-workers/aws-lambda). @@ -225,6 +230,8 @@ zip -r function.zip lib/ node_modules/ workflow-bundle.js ### ii. Deploy Lambda function {#deploy-lambda-function-step} +Replace the placeholder values and run the following command to create the Lambda function in your AWS environment. + From a7fba6903adbf43aaeb0e121d6cd486b94ac5152 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Tue, 28 Apr 2026 13:46:13 -0700 Subject: [PATCH 23/27] clarify go sample otel instructions --- .../workers/serverless-workers/aws-lambda.mdx | 56 ++++++++++++++++++- .../workers/serverless-workers/aws-lambda.mdx | 2 +- .../workers/serverless-workers/aws-lambda.mdx | 2 +- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/docs/develop/go/workers/serverless-workers/aws-lambda.mdx b/docs/develop/go/workers/serverless-workers/aws-lambda.mdx index 45cea73bd5..c4f9485291 100644 --- a/docs/develop/go/workers/serverless-workers/aws-lambda.mdx +++ b/docs/develop/go/workers/serverless-workers/aws-lambda.mdx @@ -140,7 +140,59 @@ func main() { `ApplyDefaults` configures both metrics and tracing. By default, telemetry is sent to `localhost:4317`, which is the ADOT Lambda layer's default collector endpoint. -To collect this telemetry, attach the [AWS Distro for OpenTelemetry Lambda layer](https://aws-otel.github.io/docs/getting-started/lambda/lambda-go) to your Lambda function. -The layer runs a collector sidecar that receives telemetry on `localhost:4317` and forwards it to your configured backend (e.g., AWS X-Ray, Amazon CloudWatch). +To collect this telemetry, attach the [ADOT Collector layer](https://aws-otel.github.io/docs/getting-started/lambda) to your Lambda function. +The layer runs a collector sidecar that receives telemetry on `localhost:4317` and forwards traces to X-Ray and metrics to CloudWatch. +Go does not need a language-specific ADOT layer because the OTel SDK is compiled into the binary. + +The default Collector configuration does not route OpenTelemetry Protocol (OTLP) data to the traces pipeline. +You must provide a custom Collector configuration that wires the OTLP receiver to both the traces and metrics pipelines. +Bundle the following `otel-collector-config.yaml` in your Lambda deployment package: + +```yaml +receivers: + otlp: + protocols: + grpc: + endpoint: 'localhost:4317' + http: + endpoint: 'localhost:4318' + +exporters: + debug: + awsxray: + region: + awsemf: + namespace: TemporalWorkerMetrics + log_group_name: /aws/lambda/ + region: + dimension_rollup_option: NoDimensionRollup + resource_to_telemetry_conversion: + enabled: true + +service: + pipelines: + traces: + receivers: [otlp] + exporters: [awsxray, debug] + metrics: + receivers: [otlp] + exporters: [awsemf] +``` + +Set the following environment variable on the Lambda function to point the Collector at the bundled config: + +- `OPENTELEMETRY_COLLECTOR_CONFIG_URI=/var/task/otel-collector-config.yaml` + +Enable X-Ray active tracing on the Lambda function: + +```bash +aws lambda update-function-configuration \ + --function-name \ + --tracing-config Mode=Active +``` + +The Lambda execution role must have permissions to write to X-Ray and CloudWatch. +Add `xray:PutTraceSegments`, `xray:PutTelemetryRecords`, and `cloudwatch:PutMetricData` permissions to the execution role. +Without these permissions, the Collector fails silently and no telemetry appears. If you only need metrics or tracing, use `otel.ApplyMetrics` or `otel.ApplyTracing` individually. diff --git a/docs/develop/python/workers/serverless-workers/aws-lambda.mdx b/docs/develop/python/workers/serverless-workers/aws-lambda.mdx index 93ceb66843..c6ede1d6c7 100644 --- a/docs/develop/python/workers/serverless-workers/aws-lambda.mdx +++ b/docs/develop/python/workers/serverless-workers/aws-lambda.mdx @@ -158,7 +158,7 @@ By default, telemetry is sent to `localhost:4317`, which is the ADOT Lambda laye To collect this telemetry, attach the [ADOT Python Lambda layer](https://aws-otel.github.io/docs/getting-started/lambda/lambda-python) to your Lambda function. The layer includes both auto-instrumentation and an OpenTelemetry Collector that receives telemetry on `localhost:4317` and forwards traces to AWS X-Ray and metrics to Amazon CloudWatch. -The default Collector configuration does not route OTLP data to the traces pipeline. +The default Collector configuration does not route OpenTelemetry Protocol (OTLP) data to the traces pipeline. You must provide a custom Collector configuration that wires the OTLP receiver to both the traces and metrics pipelines. Bundle the following `otel-collector-config.yaml` in your Lambda deployment package: diff --git a/docs/develop/typescript/workers/serverless-workers/aws-lambda.mdx b/docs/develop/typescript/workers/serverless-workers/aws-lambda.mdx index cac36d59ba..dce08ac844 100644 --- a/docs/develop/typescript/workers/serverless-workers/aws-lambda.mdx +++ b/docs/develop/typescript/workers/serverless-workers/aws-lambda.mdx @@ -138,7 +138,7 @@ export const handler = runWorker( ); ``` -`applyDefaults` registers Temporal SDK interceptors for tracing and configures the Core SDK to export metrics via OTLP. +`applyDefaults` registers Temporal SDK interceptors for tracing and configures the Core SDK to export metrics via OpenTelemetry Protocol (OTLP). By default, telemetry is sent to `localhost:4317`, which is the ADOT Lambda layer's default collector endpoint. To collect this telemetry, attach two ADOT Lambda layers: From b8225152aeb736f5fc30502451d8b93cea2ce548 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Tue, 28 Apr 2026 13:55:55 -0700 Subject: [PATCH 24/27] copyedits --- .../serverless-workers/index.mdx | 108 +++++++++++------- .../config/vocabularies/Temporal/accept.txt | 2 +- 2 files changed, 67 insertions(+), 43 deletions(-) diff --git a/docs/evaluate/development-production-features/serverless-workers/index.mdx b/docs/evaluate/development-production-features/serverless-workers/index.mdx index 833f04d954..ee6b7fef43 100644 --- a/docs/evaluate/development-production-features/serverless-workers/index.mdx +++ b/docs/evaluate/development-production-features/serverless-workers/index.mdx @@ -3,7 +3,9 @@ id: index title: Serverless Workers sidebar_label: Serverless Workers slug: /evaluate/serverless-workers -description: Understand the benefits of Serverless Workers and when to use them. Run Temporal Workers on serverless compute with no infrastructure to manage. +description: + Understand the benefits of Serverless Workers and when to use them. Run Temporal Workers on serverless compute with no + infrastructure to manage. toc_max_heading_level: 4 keywords: - serverless @@ -17,81 +19,103 @@ tags: - Serverless --- -Serverless Workers let you run Temporal Workers on serverless compute platforms like AWS Lambda. -There are no servers to provision, no clusters to scale, and no idle compute to pay for. -Temporal invokes the Worker when Tasks arrive, and the Worker shuts down when the work is done. +Serverless Workers let you run Temporal Workers on serverless compute platforms like AWS Lambda. There are no servers to +provision, no clusters to scale, and no idle compute to pay for. Temporal invokes the Worker when Tasks arrive, and the +Worker shuts down when the work is done. -Serverless Workers use the same Temporal SDKs as traditional long-lived Workers. -You register Workflows and Activities the same way. -The difference is in the lifecycle: instead of running a long-lived process, Temporal invokes the Serverless Worker on demand when Tasks arrive. The Worker starts, polls for available Tasks, processes them, and exits when the work is done. +Serverless Workers use the same Temporal SDKs as traditional long-lived Workers. You register Workflows and Activities +the same way. The difference is in the lifecycle: instead of running a long-lived process, Temporal invokes the +Serverless Worker on demand when Tasks arrive. The Worker starts, polls for available Tasks, processes them, and exits +when the work is done. -For a deeper look at how Serverless invocation works under the hood, see [Serverless Workers](/serverless-workers) in the encyclopedia. +For a deeper look at how Serverless invocation works under the hood, see [Serverless Workers](/serverless-workers) in +the encyclopedia. -## Benefits +## Why use Serverless Workers? + +Serverless Workers are a good fit for many workloads. They offer several advantages compared to long-lived Workers on +dedicated compute. ### Reduce operational overhead -Long-lived Workers require you to provision infrastructure, configure scaling policies, manage deployments, and monitor host-level health. -Serverless Workers reduce this burden by offloading invocation and scaling to Temporal and the compute provider. -You still deploy the function and configure the compute provider, but there is no always-on infrastructure to manage and no autoscaling policies to tune. +Long-lived Workers require you to provision infrastructure, configure scaling policies, manage deployments, and monitor +host-level health. Serverless Workers reduce this burden by offloading invocation and scaling to Temporal and the +compute provider. You still deploy the function and configure the compute provider, but there is no always-on +infrastructure to manage and no autoscaling policies to tune. -Worker management is one of the most common sources of support questions for Temporal users. -Serverless Workers offer a prescriptive deployment path that reduces the operational surface area and lets you focus on writing Workflows instead of managing infrastructure. +Worker management is one of the most common sources of support questions for Temporal users. Serverless Workers offer a +prescriptive deployment path that reduces the operational surface area and lets you focus on writing Workflows instead +of managing infrastructure. ### Get started faster -Running a long-lived Worker requires choosing a hosting strategy, configuring compute resources, and setting up deployment pipelines before you can execute your first Workflow. +Running a long-lived Worker requires choosing a hosting strategy, configuring compute resources, and setting up +deployment pipelines before you can execute your first Workflow in production. -With Serverless Workers, deploying a Worker is as simple as deploying a function. -Package your Worker code, deploy it to your serverless provider, and configure the connection to Temporal. There is no need to set up Kubernetes, manage container orchestration, or design a scaling strategy. +With Serverless Workers, deploying a Worker is as simple as deploying a function. Package your Worker code, deploy it to +your serverless provider, and configure the connection to Temporal. There is no need to set up Kubernetes, manage +container orchestration, or design a scaling strategy. ### Scale automatically -Serverless compute providers handle scaling natively. -When Task volume increases, the provider spins up additional function instances. -When traffic drops, instances scale down. When there is no work, there is no compute running. +Serverless compute providers handle scaling natively. When Task volume increases, the provider spins up additional +function instances. When traffic drops, instances scale down. When there is no work, there is no compute running. -This automatic scaling is especially useful for bursty, event-driven workloads where traffic patterns are unpredictable or highly variable. +This automatic scaling is especially useful for bursty, event-driven workloads where traffic patterns are unpredictable +or highly variable. ### Pay only for what you use -Long-lived Workers run continuously, whether or not there is work to process. -Serverless Workers run only when Tasks are available. -For workloads with low or intermittent volume, this pay-per-invocation model can significantly reduce compute costs. +Long-lived Workers run continuously, whether or not there is work to process. Serverless Workers run only when Tasks are +available. For workloads with low or intermittent volume, this pay-per-invocation model can significantly reduce compute +costs. ## When to use Serverless Workers Serverless Workers are a good fit when: -- **Workloads are bursty or event-driven.** Order processing, notifications, webhook handlers, and similar workloads that experience spiky traffic benefit from automatic scaling without over-provisioning. -- **Traffic is low or intermittent.** If Workers spend most of their time idle, Serverless Workers eliminate the cost of always-on compute. -- **You want a simpler getting-started path.** Deploying a function is simpler than setting up a container orchestration platform. Serverless Workers reduce the steps between writing Worker code and running your first Workflow. -- **Your organization has standardized on serverless.** Teams that already run services on Lambda, Cloud Run, or similar platforms can run Temporal Workers using the same deployment patterns and tooling. -- **You serve multiple tenants with infrequent workloads.** Platforms that run Workflows on behalf of many users or customers can avoid running dedicated Workers per tenant. +- **Workloads are bursty or event-driven.** Order processing, notifications, webhook handlers, and similar workloads + that experience spiky traffic benefit from automatic scaling without over-provisioning. +- **Traffic is low or intermittent.** If Workers spend most of their time idle, Serverless Workers eliminate the cost of + always-on compute. +- **You want a simpler getting-started path.** Deploying a function is simpler than setting up a container orchestration + platform. Serverless Workers reduce the steps between writing Worker code and running your first Workflow. +- **Your organization has standardized on serverless.** Teams that already run services on Lambda, Cloud Run, or similar + platforms can run Temporal Workers using the same deployment patterns and tooling. +- **You serve multiple tenants with infrequent workloads.** Platforms that run Workflows on behalf of many users or + customers can avoid running dedicated Workers per tenant. Serverless Workers may not be ideal when: -- **Activities are long-running and cannot be interrupted.** Some serverless platforms enforce execution time limits. For example, AWS Lambda has a 15-minute execution limit. Activities that run longer than the provider's timeout and cannot be broken into smaller steps need a different hosting strategy or a provider with longer limits (such as Cloud Run). Long-running Workflows are not affected because Workflows can span multiple invocations. -- **Workloads require sustained high throughput.** For consistently high-volume Task Queues, long-lived Workers on dedicated compute may be more cost-effective and performant. -- **You need persistent connections.** Some features require a persistent connection between the Worker and Temporal, which serverless invocations do not maintain. +- **Activities are long-running and cannot be interrupted.** Some serverless platforms enforce execution time limits. + For example, AWS Lambda has a 15-minute execution limit. Activities that run longer than the provider's timeout and + cannot be broken into smaller steps need a different hosting strategy or a provider with longer limits (such as Cloud + Run). Long-running Workflows are not affected because Workflows can span multiple invocations. +- **Workloads require sustained high throughput.** For consistently high-volume Task Queues, long-lived Workers on + dedicated compute may be more cost-effective and performant. +- **You need persistent connections.** Some features require a persistent connection between the Worker and Temporal, + which serverless invocations do not maintain. ## How Serverless Workers compare to long-lived Workers -| | Long-lived Worker | Serverless Worker | -|---|---|---| -| **Lifecycle** | Long-lived process that runs continuously. | Invoked on demand. Starts and stops per invocation. | -| **Scaling** | You manage scaling (Kubernetes HPA, instance count, etc.). | Temporal invokes additional instances as needed, within the compute provider's concurrency limits. | -| **Connection** | Persistent connection to Temporal. | Fresh connection on each invocation. | +| | Long-lived Worker | Serverless Worker | +| -------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | +| **Lifecycle** | Long-lived process that runs continuously. | Invoked on demand. Starts and stops per invocation. | +| **Scaling** | You manage scaling (Kubernetes HPA, instance count, etc.). | Temporal invokes additional instances as needed, within the compute provider's concurrency limits. | +| **Connection** | Persistent connection to Temporal. | Fresh connection on each invocation. | ## Supported providers -| Provider | Status | -|---|---| +| Provider | Status | +| ---------- | --------- | | AWS Lambda | Available | ## Next steps - [Interactive demo](/evaluate/serverless-workers/demo) to explore the configuration and invocation flow. -- [How Serverless Workers work](/serverless-workers) for a deeper look at the invocation lifecycle, compute providers, and architecture. -- [Deploy a Serverless Worker](/production-deployment/worker-deployments/serverless-workers) for the end-to-end deployment guide. -- [Serverless Workers - Go SDK](/develop/go/workers/serverless-workers/aws-lambda) for SDK-specific configuration and defaults. +- [How Serverless Workers work](/serverless-workers) for a deeper look at the invocation lifecycle, compute providers, + and architecture. +- [Deploy a Serverless Worker](/production-deployment/worker-deployments/serverless-workers) for the end-to-end + deployment guide. +- [Serverless Workers - Go SDK](/develop/go/workers/serverless-workers/aws-lambda) for SDK-specific configuration and + defaults. diff --git a/vale/styles/config/vocabularies/Temporal/accept.txt b/vale/styles/config/vocabularies/Temporal/accept.txt index 264999c357..7982118437 100644 --- a/vale/styles/config/vocabularies/Temporal/accept.txt +++ b/vale/styles/config/vocabularies/Temporal/accept.txt @@ -7,7 +7,7 @@ sample samples attempt multiple -serverless +[Ss]erverless Namespace gRPC tctl From 2ff0f7c2016730a4f192963773bcf0cb4e381acc Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Tue, 28 Apr 2026 14:11:02 -0700 Subject: [PATCH 25/27] fix: darken interactive demo button in dark mode Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/elements/serverless-worker-demo.module.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/elements/serverless-worker-demo.module.css b/src/components/elements/serverless-worker-demo.module.css index 9922156327..6f09d0843c 100644 --- a/src/components/elements/serverless-worker-demo.module.css +++ b/src/components/elements/serverless-worker-demo.module.css @@ -157,10 +157,18 @@ transition: background 0.2s; } +:global(html[data-theme='dark']) .executeBtn { + background: #2563eb; +} + .executeBtn:hover:not(.executeBtnDisabled) { background: var(--ifm-color-primary-dark); } +:global(html[data-theme='dark']) .executeBtn:hover:not(.executeBtnDisabled) { + background: #1d4ed8; +} + .executeBtnDisabled { opacity: 0.65; cursor: not-allowed; From c2b027ad9b8da215f914b6891eb704c1f1627857 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Tue, 28 Apr 2026 14:30:15 -0700 Subject: [PATCH 26/27] docs: update interactive demo --- .../serverless-workers/aws-lambda.mdx | 1 - .../elements/ServerlessWorkerDemo.js | 5 ++-- .../serverless-worker-demo.module.css | 26 ++++++++++++++++++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx index dcf382041d..c661d8bc5a 100644 --- a/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx +++ b/docs/production-deployment/worker-deployments/serverless-workers/aws-lambda.mdx @@ -313,7 +313,6 @@ The following parameters apply to all SDKs: | `TEMPORAL_TLS_CLIENT_KEY_PATH` | Path to the TLS client key file for mTLS authentication. | | `TEMPORAL_API_KEY` | API key for API key authentication. | -The SDKs read environment variables automatically at startup. For the full list, see [Client environment configuration](/references/client-environment-configuration). The serverless Worker packages read environment variables automatically at startup. For the full list, see [Client environment configuration](/references/client-environment-configuration). 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. diff --git a/src/components/elements/ServerlessWorkerDemo.js b/src/components/elements/ServerlessWorkerDemo.js index 66db759d67..d5717bd7aa 100644 --- a/src/components/elements/ServerlessWorkerDemo.js +++ b/src/components/elements/ServerlessWorkerDemo.js @@ -47,12 +47,11 @@ aws lambda create-function \\ --function-name ${lambdaFunctionName} \\ --runtime provided.al2023 \\ --handler bootstrap \\ - --architectures x86_64 \\ --role arn:aws:iam:::role/my-temporal-worker-execution \\ --zip-file fileb://function.zip \\ - --timeout 60 \\ + --timeout 600 \\ --memory-size 256 \\ - --environment "Variables={TEMPORAL_ADDRESS=${namespace}.tmprl.cloud:7233,TEMPORAL_NAMESPACE=${namespace}}"`; + --environment "Variables={HOME=/tmp,TEMPORAL_ADDRESS=${namespace}.tmprl.cloud:7233,TEMPORAL_NAMESPACE=${namespace},TEMPORAL_API_KEY=}"`; } function generateIamScript(config) { diff --git a/src/components/elements/serverless-worker-demo.module.css b/src/components/elements/serverless-worker-demo.module.css index 6f09d0843c..ac7b6919fc 100644 --- a/src/components/elements/serverless-worker-demo.module.css +++ b/src/components/elements/serverless-worker-demo.module.css @@ -138,6 +138,11 @@ border-color: var(--ifm-color-primary); } +:global(html[data-theme='dark']) .codeTabActive { + background: #2563eb; + border-color: #2563eb; +} + /* ── Execute button ─────────────────────────────────────────────────────── */ .executeBtn { @@ -370,11 +375,22 @@ box-shadow: 0 0 0 1px var(--ifm-color-primary); } +:global(html[data-theme='dark']) .stepActive { + border-color: #2563eb; + background: rgba(37, 99, 235, 0.12); + box-shadow: 0 0 0 1px #2563eb; +} + .stepActive:hover { border-color: var(--ifm-color-primary); background: var(--ifm-color-primary-lightest, rgba(55, 125, 255, 0.06)); } +:global(html[data-theme='dark']) .stepActive:hover { + border-color: #2563eb; + background: rgba(37, 99, 235, 0.18); +} + .stepNumber { flex-shrink: 0; width: 24px; @@ -395,6 +411,10 @@ background: var(--ifm-color-primary); } +:global(html[data-theme='dark']) .stepNumberActive { + background: #2563eb; +} + .stepContent { flex: 1; min-width: 0; @@ -408,7 +428,7 @@ .stepDesc { font-size: 0.78rem; - color: var(--ifm-font-color-secondary); + color: var(--ifm-font-color-base); line-height: 1.4; margin-top: 4px; } @@ -419,3 +439,7 @@ color: var(--ifm-color-primary); font-weight: 700; } + +:global(html[data-theme='dark']) .codeStepLabel { + color: #60a5fa; +} From 8f33a4073459017f6dad1b81efa6b625897aed61 Mon Sep 17 00:00:00 2001 From: Lenny Chen Date: Tue, 28 Apr 2026 19:28:29 -0700 Subject: [PATCH 27/27] docs: use captioned image component --- .../workers/serverless-workers.mdx | 40 ++++++------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/docs/encyclopedia/workers/serverless-workers.mdx b/docs/encyclopedia/workers/serverless-workers.mdx index c15602b48d..9199ac3b6c 100644 --- a/docs/encyclopedia/workers/serverless-workers.mdx +++ b/docs/encyclopedia/workers/serverless-workers.mdx @@ -17,7 +17,7 @@ tags: - Serverless --- -import ThemedImage from '@theme/ThemedImage'; +import CaptionedImage from '@site/src/components/images/CaptionedImage'; This page covers the following: @@ -53,19 +53,12 @@ Temporal does not need to know anything about the Worker's infrastructure. With Serverless Workers, Temporal starts the Worker. -
- -
- Temporal's Worker Controller Instance invokes a Serverless Worker when Tasks arrive on a Task Queue with a compute - provider configured. -
-
+ Temporal's internal Worker Controller Instance (WCI) decides when to start, scale, and stop compute invocations. @@ -120,19 +113,12 @@ If both scale dynamically, the long-lived Workers may scale up to handle the sam A single Serverless Worker invocation has three phases: init, work, and shutdown. -
- -
- The shutdown deadline buffer controls when the Worker stops polling, and the Worker stop timeout controls how long - the Worker waits for in-flight Tasks to finish before shutdown hooks run. -
-
+ During the **init** phase, the Worker initializes and establishes a client connection to Temporal.