TL;DR:
- Temporal is an open-source workflow orchestration engine that makes long-running processes durable by persisting execution state — a crash mid-workflow replays from the last checkpoint, not from zero
- You write normal application code (Python, TypeScript, Go, Java, PHP); Temporal handles retries, timeouts, and state management as infrastructure concerns
- Best fit: order fulfilment, data pipelines, user onboarding sequences, background jobs that must complete reliably, and anything that currently has a rats’ nest of retry queues and cron jobs
- Self-host with Docker Compose for development; Temporal Cloud is the managed option in production (pricing scales with active workflow count)
- 67% of enterprise teams surveyed in 2026 are investing in workflow orchestration — the category has moved from “interesting” to “infrastructure”
Most background job systems make the same implicit promise: we will try to run this. What they actually deliver is: we will try, and if something fails, you figure out the retry logic, the dead-letter queue, and the state reconstruction. Temporal makes a different promise: we will run this to completion, and if anything fails we will replay from where it stopped.
That is a meaningful difference when your workflow involves charging a payment, sending a confirmation email, provisioning infrastructure, and notifying a third-party API — steps that must all succeed, in order, and where partial failure leaves data in an inconsistent state.
What Durable Execution Means
Temporal calls its model “durable execution.” The core idea is that workflow state is persisted to Temporal’s event history after every step. If your application server crashes mid-workflow, restarts, and picks up where it left off — Temporal replays the event history to restore exactly the state the workflow was in before the crash. Your code does not need to handle this; Temporal’s SDK intercepts function calls and manages the replay.
This means you write a workflow that looks like a normal function:
from temporalio import workflow, activity
from datetime import timedelta
@workflow.defn
class OrderFulfilmentWorkflow:
@workflow.run
async def run(self, order_id: str) -> str:
# Each activity runs with automatic retry on failure
payment = await workflow.execute_activity(
charge_payment,
order_id,
start_to_close_timeout=timedelta(seconds=30)
)
await workflow.execute_activity(
send_confirmation_email,
order_id,
start_to_close_timeout=timedelta(seconds=10)
)
await workflow.execute_activity(
update_inventory,
order_id,
start_to_close_timeout=timedelta(seconds=10)
)
return f"Order {order_id} complete"
Each execute_activity call is durable. If send_confirmation_email fails due to a transient SMTP error, Temporal retries it automatically with configurable backoff. If your server crashes between the payment and the email, Temporal replays from the beginning — but since charge_payment already succeeded and its result is in the event history, it does not re-execute. The payment is not charged twice.
Activities and Workflows
The Temporal model separates code into two categories:
Workflows define the sequence and logic. They must be deterministic — same inputs, same outputs every time — because Temporal may replay them. This means no random number generation, no direct I/O, no calls to external services directly inside workflow code.
Activities are where the actual work happens: database writes, API calls, file operations. Activities are allowed to be non-deterministic. They run as separate, isolated units with their own retry policies, timeouts, and heartbeating for long-running operations.
This distinction feels constraining at first. In practice, it cleanly separates orchestration logic from implementation details, which makes both easier to test. Workflows test without network calls; activities test without orchestration complexity.
What Temporal Is Good For
Order and transaction processing is the canonical use case. Multi-step processes where each step has side effects and partial failure is catastrophic fit Temporal’s model precisely.
User onboarding sequences that span days or weeks — send welcome email, wait 3 days, send tutorial prompt, wait 7 days, check if they’ve activated a feature, send targeted nudge if not — are trivial to express as Temporal workflows. The alternative is a combination of cron jobs, flags in a database, and significant drift between what you intended and what runs.
Data pipeline orchestration where tasks have dependencies and some steps run for minutes or hours. Temporal’s activity heartbeat mechanism handles long-running activities gracefully — an activity can report progress every 30 seconds, and Temporal knows to retry from the last heartbeat rather than from zero if the worker dies.
Async job processing that needs reliable exactly-once semantics. Standard message queues give you at-least-once delivery; making that exactly-once requires idempotency keys and deduplication logic that most implementations get wrong. Temporal handles this correctly by default.
Local Setup
Temporal runs locally for development via Docker:
git clone https://github.com/temporalio/docker-compose.git
cd docker-compose
docker-compose up
This starts the Temporal server, web UI (at http://localhost:8080), and dependencies. The web UI shows running and historical workflows, their event histories, and lets you trigger workflows manually — useful during development.
Workers connect to this server and register their workflows and activities:
from temporalio.client import Client
from temporalio.worker import Worker
async def main():
client = await Client.connect("localhost:7233")
async with Worker(
client,
task_queue="order-processing",
workflows=[OrderFulfilmentWorkflow],
activities=[charge_payment, send_confirmation_email, update_inventory],
):
await asyncio.Event().wait()
Temporal Cloud vs Self-Hosted
The Temporal open-source project handles the client SDKs and server, which you can self-host. Temporal Cloud is the managed service: you pay per active workflow and active workflow action, and they manage availability, scaling, and upgrades.
Self-hosted is appropriate for: organisations with strict data residency requirements, teams that want complete control, or high-volume deployments where the maths on Temporal Cloud pricing does not work out. The operational overhead of running Temporal’s Cassandra or PostgreSQL backend, handling upgrades, and managing availability is real — it is not a simple weekend project for production use.
Temporal Cloud starts at roughly $25/month for small workloads and scales based on usage. For most teams getting started, the managed service is the right default until you have reason to bring it in-house.
What Temporal Is Not
Temporal is not a job queue replacement for simple background tasks. If you are running 50ms tasks that can fail and retry without consequence, Redis-backed Sidekiq or BullMQ is simpler and cheaper.
Temporal is not a cron scheduler for independent periodic tasks. It has timer support, but for “run this function every hour,” a standard cron or cloud scheduler is less overhead.
The sweet spot is workflows: sequences of steps with dependencies, retries, timeouts, and state that must survive failures. If you have code that is currently a tangle of queues, retries, and state flags, Temporal is probably the thing that makes that code boring in the best sense.