|
| 1 | +# lambda-worker |
| 2 | + |
| 3 | +A wrapper for running [Temporal](https://temporal.io) workers inside AWS Lambda. A single |
| 4 | +`runWorker` call handles the full per-invocation lifecycle: connecting to the Temporal server, |
| 5 | +creating a worker with Lambda-tuned defaults, polling for tasks, and gracefully shutting down before |
| 6 | +the invocation deadline. |
| 7 | + |
| 8 | +## Quick start |
| 9 | + |
| 10 | +```typescript |
| 11 | +// handler.ts |
| 12 | +import { runWorker } from '@temporalio/lambda-worker'; |
| 13 | +import * as activities from './activities'; |
| 14 | + |
| 15 | +export const handler = runWorker({ deploymentName: 'my-service', buildId: 'v1.0' }, (config) => { |
| 16 | + config.workerOptions.taskQueue = 'my-task-queue'; |
| 17 | + config.workerOptions.workflowBundle = { code: require('./workflow-bundle.js') }; |
| 18 | + config.workerOptions.activities = activities; |
| 19 | +}); |
| 20 | +``` |
| 21 | + |
| 22 | +Prefer `workflowBundle` (pre-bundled with `bundleWorkflowCode`) over `workflowsPath` to avoid |
| 23 | +webpack bundling overhead on Lambda cold starts. |
| 24 | + |
| 25 | +## Configuration |
| 26 | + |
| 27 | +Client connection settings (address, namespace, TLS, API key) are loaded automatically from a TOML |
| 28 | +config file and/or environment variables via `@temporalio/envconfig`. The config file is resolved in |
| 29 | +order: |
| 30 | + |
| 31 | +1. `TEMPORAL_CONFIG_FILE` env var, if set. |
| 32 | +2. `temporal.toml` in `$LAMBDA_TASK_ROOT` (typically `/var/task`). |
| 33 | +3. `temporal.toml` in the current working directory. |
| 34 | + |
| 35 | +The file is optional -- if absent, only environment variables are used. |
| 36 | + |
| 37 | +The configure callback receives a `LambdaWorkerConfig` object with fields pre-populated with |
| 38 | +Lambda-appropriate defaults. Override any field directly in the callback. The `taskQueue` in |
| 39 | +`workerOptions` is pre-populated from the `TEMPORAL_TASK_QUEUE` environment variable if set. |
| 40 | + |
| 41 | +## Lambda-tuned worker defaults |
| 42 | + |
| 43 | +The package applies conservative concurrency limits suited to Lambda's resource constraints: |
| 44 | + |
| 45 | +| Setting | Default | |
| 46 | +| -------------------------------------- | ------------------ | |
| 47 | +| `maxConcurrentActivityTaskExecutions` | 2 | |
| 48 | +| `maxConcurrentWorkflowTaskExecutions` | 10 | |
| 49 | +| `maxConcurrentLocalActivityExecutions` | 2 | |
| 50 | +| `maxConcurrentNexusTaskExecutions` | 5 | |
| 51 | +| `workflowTaskPollerBehavior` | `SimpleMaximum(2)` | |
| 52 | +| `activityTaskPollerBehavior` | `SimpleMaximum(1)` | |
| 53 | +| `nexusTaskPollerBehavior` | `SimpleMaximum(1)` | |
| 54 | +| `shutdownGraceTime` | 5 seconds | |
| 55 | +| `maxCachedWorkflows` | 30 | |
| 56 | + |
| 57 | +Worker Deployment Versioning is always enabled. The default versioning behavior is `PINNED`; to |
| 58 | +change it, override `workerDeploymentOptions.defaultVersioningBehavior` in the configure callback: |
| 59 | + |
| 60 | +```typescript |
| 61 | +config.workerOptions.workerDeploymentOptions = { |
| 62 | + defaultVersioningBehavior: 'AUTO_UPGRADE', |
| 63 | +}; |
| 64 | +``` |
| 65 | + |
| 66 | +## Logging |
| 67 | + |
| 68 | +The Temporal `Runtime` is installed automatically by `runWorker`. If |
| 69 | +[`@aws-lambda-powertools/logger`](https://docs.aws.amazon.com/powertools/typescript/latest/features/logger/) |
| 70 | +is installed, the runtime is configured with a `PowertoolsLoggerAdapter` that produces structured |
| 71 | +JSON output automatically parsed by CloudWatch Logs. If Powertools is not installed, the SDK's |
| 72 | +default human-readable logger is used. |
| 73 | + |
| 74 | +To customize the logger or other runtime options, modify `config.runtimeOptions` in the configure |
| 75 | +callback: |
| 76 | + |
| 77 | +```typescript |
| 78 | +export const handler = runWorker({ deploymentName: 'my-service', buildId: 'v1.0' }, (config) => { |
| 79 | + config.workerOptions.taskQueue = 'my-task-queue'; |
| 80 | + // Use a custom logger |
| 81 | + config.runtimeOptions.logger = myCustomLogger; |
| 82 | + // Or configure telemetry |
| 83 | + config.runtimeOptions.telemetryOptions = { ... }; |
| 84 | +}); |
| 85 | +``` |
| 86 | + |
| 87 | +Shutdown signals are disabled by default (`shutdownSignals: []`) since Lambda manages its own |
| 88 | +lifecycle. |
| 89 | + |
| 90 | +## Observability |
| 91 | + |
| 92 | +Metrics and tracing are opt-in. The `otel` module provides convenience helpers for |
| 93 | +[AWS Distro for OpenTelemetry (ADOT)](https://aws-otel.github.io/docs/getting-started/lambda). |
| 94 | + |
| 95 | +Call `applyDefaults(config)` in your configure callback to: |
| 96 | + |
| 97 | +- Register Temporal SDK interceptors (`@temporalio/interceptors-opentelemetry`) for tracing |
| 98 | + Workflow, Activity, and Nexus calls. |
| 99 | +- Configure the Temporal Core SDK (Rust) to export its own metrics (task latencies, poll counts, |
| 100 | + etc.) via OTLP to the collector. |
| 101 | + |
| 102 | +```typescript |
| 103 | +import { runWorker } from '@temporalio/lambda-worker'; |
| 104 | +import { applyDefaults } from '@temporalio/lambda-worker/otel'; |
| 105 | +import * as activities from './activities'; |
| 106 | + |
| 107 | +export const handler = runWorker({ deploymentName: 'my-service', buildId: 'v1.0' }, (config) => { |
| 108 | + applyDefaults(config); |
| 109 | + config.workerOptions.taskQueue = 'my-task-queue'; |
| 110 | + config.workerOptions.workflowBundle = { code: require('./workflow-bundle.js') }; |
| 111 | + config.workerOptions.activities = activities; |
| 112 | +}); |
| 113 | +``` |
| 114 | + |
| 115 | +**Important**: When pre-bundling Workflow code with `bundleWorkflowCode()`, pass `makeOtelPlugin()` |
| 116 | +so that Workflow interceptor modules are included in the bundle: |
| 117 | + |
| 118 | +```typescript |
| 119 | +import { bundleWorkflowCode } from '@temporalio/worker'; |
| 120 | +import { makeOtelPlugin } from '@temporalio/lambda-worker/otel'; |
| 121 | + |
| 122 | +const { plugin } = makeOtelPlugin(); |
| 123 | +const { code } = await bundleWorkflowCode({ |
| 124 | + workflowsPath: require.resolve('./workflows'), |
| 125 | + plugins: [plugin], |
| 126 | +}); |
| 127 | +``` |
| 128 | + |
| 129 | +### AWS setup |
| 130 | + |
| 131 | +Attach two Lambda layers: |
| 132 | + |
| 133 | +1. **[ADOT JavaScript layer](https://aws-otel.github.io/docs/getting-started/lambda/lambda-js)** — |
| 134 | + auto-instruments the handler and exports Node.js-side traces (Lambda invocation spans, HTTP |
| 135 | + calls, etc.) to X-Ray. |
| 136 | +2. **[ADOT Collector layer](https://aws-otel.github.io/docs/getting-started/lambda)** |
| 137 | + (`aws-otel-collector-amd64`) — runs the OTel Collector as a Lambda extension, receiving |
| 138 | + Temporal Core SDK metrics and SDK trace spans via OTLP gRPC on `localhost:4317`, then |
| 139 | + forwarding them to CloudWatch/X-Ray via a custom collector config. |
| 140 | + |
| 141 | +Set these environment variables: |
| 142 | + |
| 143 | +``` |
| 144 | +AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-instrument |
| 145 | +OPENTELEMETRY_COLLECTOR_CONFIG_URI=/var/task/otel-collector-config.yaml |
| 146 | +``` |
| 147 | + |
| 148 | +`AWS_LAMBDA_EXEC_WRAPPER` enables the JS layer's auto-instrumentation. |
| 149 | +`OPENTELEMETRY_COLLECTOR_CONFIG_URI` points the collector at a custom config file bundled in |
| 150 | +your deployment package — use this to route metrics to CloudWatch EMF, traces to X-Ray, or any |
| 151 | +other supported exporter. |
| 152 | + |
| 153 | +Enable X-Ray active tracing on the Lambda function (required for traces to appear): |
| 154 | + |
| 155 | +```bash |
| 156 | +aws lambda update-function-configuration --function-name <function-name> \ |
| 157 | + --tracing-config Mode=Active |
| 158 | +``` |
0 commit comments