# Serverless Workers on AWS Lambda - TypeScript SDK

> Write a Temporal Worker that runs on AWS Lambda using the TypeScript SDK @temporalio/lambda-worker package.

> **Pre-release**
> To request access during Pre-release, create a [support ticket](/cloud/support#support-ticket) or contact your account team.
> APIs are experimental and may be subject to backwards-incompatible changes.
> [Sign up for updates](https://temporal.io/pages/serverless-workers-updates) to be notified when Serverless Workers reach Public Preview.

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 

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.

<!--SNIPSTART typescript-lambda-worker {"selectedLines": ["1", "3-11", "13"]}-->
[lambda-worker/src/index.ts](https://github.com/temporalio/samples-typescript/blob/main/lambda-worker/src/index.ts)
```ts
import { runWorker } from '@temporalio/lambda-worker';
// ...
import * as activities from './activities';
import { TASK_QUEUE } from './workflows';

export const handler = runWorker({ deploymentName: 'sdk-demo', buildId: 'v1' }, (config) => {
  config.workerOptions.taskQueue = TASK_QUEUE;
  config.workerOptions.workflowBundle = {
    codePath: require.resolve('./workflow-bundle.js'),
  };
  config.workerOptions.activities = activities;
// ...
});
```
<!--SNIPEND-->

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 

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 

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 

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 

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/platform/observability) and the [SDK metrics reference](/references/sdk-metrics).

<!--SNIPSTART typescript-lambda-worker-->
[lambda-worker/src/index.ts](https://github.com/temporalio/samples-typescript/blob/main/lambda-worker/src/index.ts)
```ts
import { runWorker } from '@temporalio/lambda-worker';
import { applyDefaults } from '@temporalio/lambda-worker/otel';
import * as activities from './activities';
import { TASK_QUEUE } from './workflows';

export const handler = runWorker({ deploymentName: 'sdk-demo', buildId: 'v1' }, (config) => {
  config.workerOptions.taskQueue = TASK_QUEUE;
  config.workerOptions.workflowBundle = {
    codePath: require.resolve('./workflow-bundle.js'),
  };
  config.workerOptions.activities = activities;
  applyDefaults(config);
});
```
<!--SNIPEND-->

`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:

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:

<!--SNIPSTART typescript-lambda-worker-otel-collector-config-->
[lambda-worker/otel-collector-config.yaml](https://github.com/temporalio/samples-typescript/blob/main/lambda-worker/otel-collector-config.yaml)
```yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 'localhost:4317'
      http:
        endpoint: 'localhost:4318'

exporters:
  debug:
  awsxray:
    region: us-west-2
  awsemf:
    # AWS EMF exporter for metrics
    # These are example configurations
    namespace: TemporalWorkerMetrics
    log_group_name: /aws/lambda/<your-function-name>
    region: us-west-2
    dimension_rollup_option: NoDimensionRollup
    resource_to_telemetry_conversion:
      enabled: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [awsxray, debug]
    metrics:
      receivers: [otlp]
      exporters: [awsemf]
  telemetry:
    logs:
      level: info
    metrics:
      address: localhost:8888
```
<!--SNIPEND-->

Set the following environment variable on the Lambda function:

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