Worker setup example in Rust
Example showing full Worker setup:
```rust
use std::str::FromStr;
use temporalio_client::{Client, ClientOptions, Connection, ConnectionOptions};
use temporalio_common::telemetry::TelemetryOptions;
use temporalio_sdk::{Worker, WorkerOptions};
use temporalio_sdk_core::{CoreRuntime, RuntimeOptions, Url};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let runtime = CoreRuntime::new_assume_tokio(
RuntimeOptions::builder()
.telemetry_options(TelemetryOptions::builder().build())
.build()?,
)?;
let connection_options =
ConnectionOptions::new(Url::from_str("http://localhost:7233")?).build();
let connection = Connection::connect(connection_options).await?;
let client = Client::new(connection, ClientOptions::new("default").build())?;
let worker_options = WorkerOptions::new("my-task-queue")
.register_workflow::<GreetingWorkflow>()?
.register_activities(GreetingActivities)
.build();
let mut worker = Worker::new(&runtime, client, worker_options)?;
worker.run().await?;
Ok(())
}
```
This example creates a CoreRuntime, connects to a local Temporal server on port 7233, registers a workflow and activities, and runs the worker.
Shut down a Worker in Rust SDK
Call shutdown_handle() on the Worker before calling run(), since run() borrows the Worker mutably. The shutdown handle returned initiates shutdown when called, stopping polling for new Tasks and allowing in-flight Tasks to finish. Use this pattern to perform graceful shutdown on signals like SIGINT.
Worker options configuration
WorkerOptions controls Task Queue, cache size, poller behavior, and slot allocation. Key options include max_cached_workflows, workflow_task_poller_behavior, tuner, and client_identity_override to set a more useful Worker identity than the default of {pid}@{hostname}. The defaults work for most cases; tune against real load using Worker performance and worker tuning reference documentation.
Task queue routing behavior and failure handling
Task queues do not route tasks by type. Any Worker polling a task queue can receive any Task on that queue. If a Worker receives a Task for a type it did not register, that Task fails.
Register Workflows and Activities on a Worker
Use register_workflow::<T>() for each Workflow type, which returns a Result requiring error handling with ?. Use register_activities() with an instance, which allows Activities to share state through instance fields. All Workers polling the same Task Queue must register the same Workflow and Activity types, since task queues do not route by type and any Worker can receive any task from the queue.
Create and run a Worker in Rust SDK
To create and run a Worker: (1) instantiate CoreRuntime with RuntimeOptions and TelemetryOptions, (2) create a Connection with ConnectionOptions pointing to the Temporal server address, (3) create a Client with the Connection and namespace, (4) build WorkerOptions with the task queue name, register workflows and activities, (5) create a Worker instance, and (6) call worker.run() which polls until shutdown. The run() method blocks and polls the task queue for work.
Standalone Activity Worker setup - TypeScript
Running a Worker for Standalone Activities is the same as for Workflow Activities. Create a Worker, register the Activity, and run the Worker. The Worker does not need to know whether the Activity will be invoked from a Workflow or as a Standalone Activity. Example:
```typescript
import { NativeConnection, Worker } from '@temporalio/worker';
import * as activities from './activities';
import { loadClientConnectConfig } from '@temporalio/envconfig';
async function run() {
const config = loadClientConnectConfig();
const connection = await NativeConnection.connect(config.connectionOptions);
try {
const worker = await Worker.create({
connection,
namespace: 'default',
taskQueue: 'hello-standalone-activities',
activities,
});
await worker.run();
} finally {
await connection.close();
}
}
run().catch((err) => {
console.error(err);
process.exit(1);
});
```
TypeScript Client main topics
The primary client topics in TypeScript SDK documentation are: Temporal Client and Namespaces.
TypeScript SDK Client overview
The TypeScript SDK Client section provides documentation for implementing clients with the TypeScript SDK. Key topics include the Temporal Client and Namespaces.
Namespace operations available
Namespaces can be created, updated, depreciated, deleted, and managed. Details can be retrieved, configuration can be updated, and Namespaces can be deprecated or deleted.
Register Namespace before setting in Temporal Client
A Namespace must be registered with the Temporal Service before it can be set in the Temporal Client.
Namespace management on Temporal Cloud
On Temporal Cloud, Namespaces can be created and managed using the Temporal Cloud UI or tcld commands from the command-line interface.
Namespace management on self-hosted Temporal Service
On self-hosted Temporal Service, Namespaces can be registered and managed using the Temporal CLI (recommended) or programmatically using APIs. Note that these APIs and Temporal CLI commands will not work with Temporal Cloud.
Temporal SDK packages composition
The Temporal TypeScript SDK comprises five main packages: @temporalio/client for communicating with the Temporal Service, @temporalio/worker for Worker Process management, @temporalio/workflow for workflow development, @temporalio/activity for activity authoring, and @temporalio/common for shared utilities.
TypeScript SDK installation with npm
To install the Temporal TypeScript SDK, run 'npm install @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity @temporalio/common'. The project requires Node.js 18 or later.
TypeScript SDK API reference location
The Temporal TypeScript SDK API reference is published at typescript.temporal.io.
Worker.runUntil() for finite workflow execution
Worker.runUntil() executes the provided async function and stops the Worker when it completes. This is useful for scripts that need to execute a specific workflow and then shut down, in contrast to Worker.run() which runs indefinitely.
Connect to Temporal server from TypeScript client
Use Connection.connect({ address: 'localhost:7233' }) to create a connection to a Temporal server. Then create a Client from the connection with the desired namespace parameter.
TypeScript SDK platform documentation sections
The TypeScript SDK platform documentation covers two main areas: Observability and Enriching the UI.
Hello World Client example
Example Client implementation:
import { Client, Connection } from '@temporalio/client';
import { nanoid } from 'nanoid';
import { example } from './workflows';
async function run() {
const connection = await Connection.connect({ address: 'localhost:7233' });
const client = new Client({
connection,
});
const handle = await client.workflow.start(example, {
taskQueue: 'hello-world',
args: ['Temporal'],
workflowId: 'workflow-' + nanoid(),
});
console.log(`Started workflow ${handle.workflowId}`);
console.log(await handle.result());
}
run().catch((err) => {
console.error(err);
process.exit(1);
});
This demonstrates connecting to the Temporal Server, starting a workflow execution, and waiting for the result.
TypeScript SDK Node.js version requirement
The Temporal TypeScript SDK requires Node.js 20 or later.
Install TypeScript SDK packages
To add Temporal to an existing project, install the required packages with: npm install @temporalio/client @temporalio/worker @temporalio/workflow
Create new Temporal TypeScript project
Create a new project with the Temporal SDK using: npx @temporalio/create@latest ./my-app. When prompted to select a sample, choose the hello-world sample.
Start local Temporal Service with CLI
Start a local Temporal Service development server using: temporal server start-dev. This starts the Web UI, creates the default Namespace, uses an in-memory database, and makes the Temporal Service available on localhost:7233. The Temporal Web UI is available at http://localhost:8233.
Change Temporal Web UI port
To change the port for the Web UI when starting the server, use the --ui-port option: temporal server start-dev --ui-port 8080. The Temporal Web UI will then be available at http://localhost:8080.
Worker creation and configuration
A Worker is created using Worker.create() with configuration including: connection (NativeConnection instance), namespace, taskQueue, workflowsPath (path to workflow definitions), and activities (activity implementations). The Worker then calls await worker.run() to start accepting tasks from the queue. Worker logs are written via the Runtime logger to STDERR at INFO level by default.
Hello World Worker example
Example Worker implementation:
import { NativeConnection, Worker } from '@temporalio/worker';
import * as activities from './activities';
async function run() {
const connection = await NativeConnection.connect({
address: 'localhost:7233',
});
try {
const worker = await Worker.create({
connection,
namespace: 'default',
taskQueue: 'hello-world',
workflowsPath: require.resolve('./workflows'),
activities,
});
await worker.run();
} finally {
await connection.close();
}
}
run().catch((err) => {
console.error(err);
process.exit(1);
});
This demonstrates establishing a NativeConnection, registering workflows and activities, and starting the worker to poll the task queue.
Worker polls Task Queue for work
A Worker polls a Task Queue for work to do. Once the Worker dequeues a Workflow or Activity task from the Task Queue, it executes that task. Workers continue running until they encounter an unexpected error or the process receives a shutdown signal.
Client connection configuration
A Temporal Client is created by first establishing a Connection using Connection.connect() with an address (default is localhost:7233). In production, configure TLS and other settings by passing options including address and tls. For TLS with default settings, pass tls: true. For custom TLS configuration, pass a TLSConfig object.
Temporal Client lifecycle
Temporal Clients are not explicitly closed.
Worker versioning configuration in TypeScript
To enable Worker Versioning in TypeScript, pass a Build ID and set useVersioning to true when creating a Worker. The buildId parameter should be assigned from an environment variable or similar configuration. Example: const worker = await Worker.create({ taskQueue: 'your_task_queue_name', buildId: buildId, useVersioning: true, ... });
TypeScript SDK workers documentation structure
The TypeScript SDK workers section includes documentation on worker processes and interceptors. Worker processes explain how to run workers, and interceptors explain how to hook into the worker lifecycle.
Activity interceptor registration
Activity interceptors are registered on Worker creation by passing an array of ActivityInboundCallsInterceptor factory functions through WorkerOptions.interceptors.
Client interceptor registration
Client interceptors are registered on Client construction by passing an array of WorkflowClientInterceptor via ClientOptions.interceptors.
Install ca-certificates with node:slim Docker images
When using node:slim Docker images, install ca-certificates package as they are not included by default. The TypeScript SDK requires root TLS certificates even when connecting to a local Temporal Service or when not using TLS. Failure to install results in a '[TransportError: transport error]' runtime error.
Example: Multi-step Dockerfile for distroless/nodejs
```dockerfile
# -- BUILD STEP --
FROM node:20-bullseye AS builder
COPY . /app
WORKDIR /app
RUN npm install --only=production \
&& npm run build
# -- RESULTING IMAGE --
FROM gcr.io/distroless/nodejs20-debian11
COPY --from=builder /app /app
WORKDIR /app
CMD ["node", "build/worker.js"]
```
This example demonstrates using distroless/nodejs for smaller Docker images by building in one stage and copying artifacts to a minimal image.
Example: Dockerfile for node:slim with ca-certificates
```dockerfile
FROM node:20-bullseye-slim
RUN apt-get update \
&& apt-get install -y ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY . /app
WORKDIR /app
RUN npm install --only=production \
&& npm run build
CMD ["npm", "start"]
```
This example shows how to install ca-certificates when using node:slim Docker images.
Example: Dockerfile for TypeScript SDK Worker with node:20-bullseye
```dockerfile
FROM node:20-bullseye
COPY . /app
WORKDIR /app
RUN npm install --only=production \
&& npm run build
CMD ["npm", "start"]
```
This example shows a basic Dockerfile for deploying a TypeScript SDK Worker using the bullseye image.
Example: Build workflow bundle script
```ts
import { bundleWorkflowCode } from '@temporalio/worker';
import { writeFile } from 'fs/promises';
import path from 'path';
async function bundle() {
const { code } = await bundleWorkflowCode({
workflowsPath: require.resolve('../workflows'),
});
const codePath = path.join(__dirname, '../../workflow-bundle.js');
await writeFile(codePath, code);
console.log(`Bundle written to ${codePath}`);
}
```
This example demonstrates pre-bundling workflow code during the build step for faster Worker startup in production.
Example: Production Worker with pre-bundled workflows
```ts
const workflowOption = () =>
process.env.NODE_ENV === 'production'
? {
workflowBundle: {
codePath: require.resolve('../workflow-bundle.js'),
},
}
: { workflowsPath: require.resolve('./workflows') };
async function run() {
const worker = await Worker.create({
...workflowOption(),
activities,
taskQueue: 'production-sample',
});
await worker.run();
}
```
This example shows conditional workflow loading: using pre-bundled code in production and runtime bundling in development.
Example: Graceful Worker shutdown with shutdownGraceTime
```ts
const worker = await Worker.create({
connection,
taskQueue: 'my-task-queue',
workflowsPath: require.resolve('./workflows'),
shutdownGraceTime: '30s',
});
await worker.run();
```
This example demonstrates setting a 30-second grace period for in-progress Activities to complete before Worker shutdown.
Example: Basic Worker setup with NativeConnection
```ts
import { NativeConnection, Worker } from '@temporalio/worker';
import * as activities from './activities';
async function run() {
const connection = await NativeConnection.connect({
address: 'localhost:7233',
});
try {
const worker = await Worker.create({
connection,
namespace: 'default',
taskQueue: 'sleep-for-days',
workflowsPath: require.resolve('./workflows'),
activities,
});
await worker.run();
} finally {
await connection.close();
}
}
run().catch((err) => {
console.error(err);
process.exit(1);
});
```
This example demonstrates connecting to Temporal, creating a Worker with workflows and activities, and running it with proper connection cleanup.
Do not use Alpine Linux for TypeScript SDK Workers
Alpine replaces glibc with musl, which is incompatible with the Rust core of the TypeScript SDK. Errors like 'Error loading shared library ld-linux-x86-64.so.2' or 'symbol not found for __register_atfork' indicate a musl-based image is being used. Use glibc-based images instead.
Configure Node.js memory limit in Docker with NODE_OPTIONS
Set the --max-old-space-size Node.js argument explicitly through the NODE_OPTIONS environment variable when running in Docker. By default, Node.js sets maximum old-gen memory to 25% of the host's physical memory (up to 4 GB) rather than the container's limit. Set it to roughly 80% of the memory in megabytes you want the process to use.
Use multi-step Dockerfile for distroless/nodejs images
When building TypeScript SDK Workers with distroless/nodejs images, use a multi-step Dockerfile: build the application in a node image first, then copy the built artifacts into the distroless image. Build tools like npm are not in distroless/nodejs images.
Docker image requirements for TypeScript SDK Workers
Use LTS Node.js releases (18, 20, 22, or 24) in Docker. Both amd64 and arm64 architectures are supported. A glibc-based image is required; musl-based images (like Alpine) are not supported due to incompatibility with the Rust core of the TypeScript SDK.
Worker states and lifecycle
A Worker progresses through seven states: INITIALIZED (after Worker.create() succeeds), RUNNING (after worker.run() is called), FAILED (unrecoverable error), STOPPING (shutdown signal received), DRAINING (all Workflow Tasks drained, waiting for Activities and cached Workflows eviction), DRAINED (all Activities and Workflows completed), STOPPED (shutdown complete, worker.run() resolves). Query Worker state with Worker.getState().
Programmatically shutdown a Worker
Call Worker.shutdown() to shut down a Worker programmatically. This is useful in integration tests or when automating a fleet of Workers.
shutdownForceTime for guaranteed Worker shutdown
Set shutdownForceTime on Worker.create() to guarantee the Worker eventually shuts down if you must ensure termination by a deadline.
shutdownGraceTime for graceful Worker shutdown
Set shutdownGraceTime (as a string like '30s') on Worker.create() to give in-progress Activities time to finish before Worker shutdown. As soon as a shutdown signal is received, the Worker stops polling for new Tasks and allows in-flight Tasks to complete until shutdownGraceTime is reached. Any Activities still running after this time are rescheduled by the Temporal Service.
Worker shutdown signals
Workers shut down when the process receives operating system signals: SIGINT, SIGTERM, SIGQUIT, or SIGUSR2. In development, use Ctrl+C (SIGINT) or nodemon (SIGUSR2). Shutdown signals are configurable through the shutdownSignals RuntimeOptions parameter.
Use bundleWorkflowCode for production startup optimization
In production, improve Worker startup time by pre-bundling workflow code. Call bundleWorkflowCode() with workflowsPath during your build step to generate a bundle file. Then pass the bundle to Worker.create() using the workflowBundle option with a codePath parameter.
Use workflowsPath in development
In development, use the workflowsPath option in Worker.create() to specify the location of workflow files. The Worker will bundle the workflow code at runtime.
All Workers on same Task Queue must register same Workflow and Activity Types
All Workers polling the same Task Queue must register the same Workflow Types and Activity Types. A Task Queue does not route by type, so any Worker polling it can receive any Task on that queue. A Worker that receives a Task for a type it did not register fails that Task.
Workflows must be registered by path, not by value
Workflows are registered by path rather than by value because they run in a separate JavaScript context. Use the workflowsPath option in Worker.create() to specify the path to the workflows file.
Create and run a Worker with Worker.create()
Create a Worker using Worker.create(), passing a connection, namespace, task queue name, workflows path or bundle, and activities object. Call await worker.run() to start polling the task queue. The connection should be created with NativeConnection.connect().
AWS Lambda support for Serverless Workers
AWS Lambda support for Serverless Workers is in Public Preview. The TypeScript SDK provides the @temporalio/lambda-worker package to run a Worker as a Lambda function, covering setup, configuration, Lambda-tuned defaults, and observability.
Serverless Workers definition and behavior
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.
Pre-bundle Workflow code for Lambda
Use workflowBundle with pre-bundled code instead of workflowsPath to avoid webpack bundling overhead on every Lambda cold start. Build the bundle as a separate build step using bundleWorkflowCode({ workflowsPath: require.resolve('./workflows') }) and write it to a file. Then reference it in the handler with workflowBundle: { codePath: require.resolve('./workflow-bundle.js') }.
shutdownDeadlineBufferMs for Lambda Worker
shutdownDeadlineBufferMs is specific to the @temporalio/lambda-worker package and controls how much time before the Lambda deadline the Worker begins graceful shutdown. The default is shutdownGraceTime (5s) plus 2s, totaling 7000ms. If your Worker handles long-running Activities, increase shutdownGraceTime, shutdownDeadlineBufferMs, and the Lambda invocation deadline (--timeout) together.