Overview
Temporal is an open-source durable execution platform that enables developers to build reliable distributed applications without worrying about infrastructure complexity. Workflows in Temporal are fault-tolerant by design — they survive process crashes, server outages, and network failures, automatically resuming exactly where they left off.
Temporal separates workflow logic (orchestration) from activities (side effects like API calls or database writes). The Temporal server maintains the complete event history of each workflow execution, enabling replay-based recovery. It supports multiple programming languages including Go, Java, TypeScript, Python, and .NET through official SDKs.
Installation
Temporal CLI (Development Server)
# macOS
brew install temporal
# Linux / Other
curl -sSf https://temporal.download/cli.sh | sh
# Start development server
temporal server start-dev
# With custom port and namespace
temporal server start-dev --port 7233 --ui-port 8233 --namespace my-namespace
# Web UI available at http://localhost:8233
Docker Compose
version: '3.5'
services:
temporal:
image: temporalio/auto-setup:latest
ports:
- "7233:7233"
environment:
- DB=postgresql
- DB_PORT=5432
- POSTGRES_USER=temporal
- POSTGRES_PWD=temporal
- POSTGRES_SEEDS=postgresql
temporal-ui:
image: temporalio/ui:latest
ports:
- "8080:8080"
environment:
- TEMPORAL_ADDRESS=temporal:7233
postgresql:
image: postgres:15
environment:
POSTGRES_USER: temporal
POSTGRES_PASSWORD: temporal
SDK Installation
# Go
go get go.temporal.io/sdk
# TypeScript
npm install @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity
# Python
pip install temporalio
# Java (Maven)
# <dependency>
# <groupId>io.temporal</groupId>
# <artifactId>temporal-sdk</artifactId>
# <version>1.24.0</version>
# </dependency>
Core Concepts
| Concept | Description |
|---|
| Workflow | Durable function that orchestrates activities |
| Activity | Non-deterministic operation (I/O, API calls) |
| Task Queue | Named queue connecting workflows/activities to workers |
| Worker | Process that polls task queues and executes code |
| Signal | Async message sent to a running workflow |
| Query | Synchronous read of workflow state |
| Schedule | Cron-like recurring workflow execution |
Workflow Definition (TypeScript)
// workflows.ts
import { proxyActivities, sleep, defineSignal, defineQuery,
setHandler, condition } from '@temporalio/workflow';
import type * as activities from './activities';
const { sendEmail, processPayment, updateInventory } = proxyActivities<
typeof activities
>({
startToCloseTimeout: '30s',
retry: {
maximumAttempts: 3,
initialInterval: '1s',
backoffCoefficient: 2,
},
});
export const cancelOrder = defineSignal('cancelOrder');
export const getStatus = defineQuery<string>('getStatus');
export async function orderWorkflow(orderId: string, items: string[]) {
let status = 'processing';
let cancelled = false;
setHandler(cancelOrder, () => { cancelled = true; });
setHandler(getStatus, () => status);
status = 'payment';
await processPayment(orderId);
if (cancelled) {
status = 'cancelled';
return { status: 'cancelled' };
}
status = 'fulfillment';
await updateInventory(items);
status = 'notification';
await sendEmail(orderId, 'Your order is confirmed!');
// Wait 24 hours then follow up
await sleep('24h');
await sendEmail(orderId, 'How was your experience?');
status = 'completed';
return { status: 'completed', orderId };
}
Activity Definition
// activities.ts
export async function sendEmail(orderId: string, message: string) {
const response = await fetch('https://api.email.com/send', {
method: 'POST',
body: JSON.stringify({ orderId, message }),
});
return response.json();
}
export async function processPayment(orderId: string) {
// Call payment API
return { success: true, transactionId: 'txn_123' };
}
export async function updateInventory(items: string[]) {
// Update inventory system
return { updated: items.length };
}
Worker Setup
// worker.ts
import { Worker } from '@temporalio/worker';
import * as activities from './activities';
async function run() {
const worker = await Worker.create({
workflowsPath: require.resolve('./workflows'),
activities,
taskQueue: 'order-processing',
maxConcurrentActivityTaskExecutions: 100,
maxConcurrentWorkflowTaskExecutions: 50,
});
await worker.run();
}
run().catch(console.error);
Client Usage
// client.ts
import { Client } from '@temporalio/client';
const client = new Client();
// Start a workflow
const handle = await client.workflow.start('orderWorkflow', {
taskQueue: 'order-processing',
workflowId: `order-${orderId}`,
args: [orderId, ['item1', 'item2']],
workflowRunTimeout: '24h',
});
// Get result
const result = await handle.result();
// Query workflow state
const status = await handle.query('getStatus');
// Send signal
await handle.signal('cancelOrder');
// Cancel workflow
await handle.cancel();
// Terminate workflow
await handle.terminate('Terminated by admin');
CLI Commands
| Command | Description |
|---|
temporal workflow start --task-queue q --type wf | Start a workflow |
temporal workflow show -w <workflow-id> | Show workflow details |
temporal workflow list | List workflows |
temporal workflow signal -w <id> --name sig | Signal a workflow |
temporal workflow query -w <id> --name q | Query workflow state |
temporal workflow cancel -w <id> | Cancel a workflow |
temporal workflow terminate -w <id> | Terminate a workflow |
temporal task-queue describe -t <queue> | Describe task queue |
temporal schedule create --schedule-id s1 | Create a schedule |
temporal schedule list | List schedules |
Configuration
Worker Tuning
const worker = await Worker.create({
taskQueue: 'main',
workflowsPath: require.resolve('./workflows'),
activities,
maxConcurrentActivityTaskExecutions: 200,
maxConcurrentWorkflowTaskExecutions: 100,
maxCachedWorkflows: 600,
stickyQueueScheduleToStartTimeout: '10s',
reuseV8Context: true,
});
Retry Policies
const activities = proxyActivities({
startToCloseTimeout: '1m',
retry: {
initialInterval: '1s',
backoffCoefficient: 2.0,
maximumInterval: '1m',
maximumAttempts: 5,
nonRetryableErrorTypes: ['InvalidInputError'],
},
});
Advanced Usage
Schedules (Cron Workflows)
temporal schedule create \
--schedule-id daily-report \
--workflow-type generateReport \
--task-queue reports \
--cron '0 8 * * *'
Child Workflows
import { executeChild } from '@temporalio/workflow';
export async function parentWorkflow(items: string[]) {
const results = await Promise.all(
items.map((item) =>
executeChild('processItem', {
args: [item],
taskQueue: 'items',
})
)
);
return results;
}
Saga Pattern (Compensation)
export async function sagaWorkflow(orderId: string) {
const compensations: Array<() => Promise<void>> = [];
try {
await reserveInventory(orderId);
compensations.push(() => releaseInventory(orderId));
await processPayment(orderId);
compensations.push(() => refundPayment(orderId));
await shipOrder(orderId);
} catch (err) {
// Run compensations in reverse
for (const compensate of compensations.reverse()) {
await compensate();
}
throw err;
}
}
Troubleshooting
| Issue | Solution |
|---|
| Workflow stuck | Check worker is running on the correct task queue; inspect event history |
| Non-determinism error | Workflow code must be deterministic; avoid random, Date.now(), or side effects |
| Activity timeout | Increase startToCloseTimeout; check activity is completing properly |
| Worker not picking up tasks | Verify task queue name matches between client and worker |
| History too large | Break into child workflows; reduce number of events per workflow |
| Schedule not triggering | Verify schedule is running (temporal schedule describe); check cron expression |
| Connection refused | Ensure Temporal server is running on the expected address and port |
| Workflow already running | Use workflowIdReusePolicy to control behavior on duplicate IDs |