Skip to main content

Serverless Workers on AWS Lambda - TypeScript SDK

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.

Create and run a Worker in Lambda

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.

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, either AutoUpgrade or Pinned. The default versioning behavior is PINNED. To change it, set workerDeploymentOptions.defaultVersioningBehavior in the configure callback.

Pre-bundle Workflow code

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:

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

The @temporalio/lambda-worker package automatically loads Temporal client configuration from a TOML config file and environment variables. Refer to 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 for options.

Adjust Worker defaults for Lambda

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.

SettingLambda default
maxConcurrentActivityTaskExecutions2
maxConcurrentWorkflowTaskExecutions10
maxConcurrentLocalActivityExecutions2
maxConcurrentNexusTaskExecutions5
workflowTaskPollerBehaviorSimpleMaximum(2)
activityTaskPollerBehaviorSimpleMaximum(1)
nexusTaskPollerBehaviorSimpleMaximum(1)
shutdownGraceTime5 seconds
maxCachedWorkflows30
shutdownDeadlineBufferMs7000

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.

Add observability with OpenTelemetry

The @temporalio/lambda-worker/otel module provides OpenTelemetry integration with defaults configured for the AWS Distro for OpenTelemetry (ADOT) 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 and the SDK metrics reference.

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, you need an OpenTelemetry Collector running alongside your Lambda function. Attach the ADOT Collector Lambda layer (aws-otel-collector-amd64) to run the Collector as a Lambda extension. The Collector receives metrics and traces via OTLP on localhost:4317 and forwards them to AWS X-Ray and Amazon CloudWatch.

The default Collector configuration does not route OTLP data to the traces pipeline. You must provide a custom Collector configuration file that wires the OTLP receiver to both the traces and metrics pipelines. Bundle the following otel-collector-config.yaml in your Lambda deployment package:

receivers:
otlp:
protocols:
grpc:
endpoint: 'localhost:4317'
http:
endpoint: 'localhost:4318'

exporters:
debug:
awsxray:
region: <your-region>
awsemf:
namespace: TemporalWorkerMetrics
log_group_name: /aws/lambda/<your-function-name>
region: <your-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_FILE=/var/task/otel-collector-config.yaml

The Lambda execution role must have permissions to write to X-Ray and CloudWatch. Attach the AWSXRayDaemonWriteAccess managed policy, or add xray:PutTraceSegments, xray:PutTelemetryRecords, and cloudwatch:PutMetricData permissions. 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:

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],
});