๐Workflow Orchestration
Three tools answer to the word "workflow," and they are not the same machine. Temporal is a durable-execution engine you write as ordinary code; Apache Airflow is a Python DAG scheduler for data pipelines; AWS Step Functions is a managed JSON state machine wired into AWS. An exhaustive, primary-source comparison of how each one models work, survives failure, scales, deploys, and bills.
TL;DR
- Temporal is a durable-execution engine. You write the workflow as a normal procedural function in your own language, and the engine makes that function itself crash-proof: it runs to completion exactly once, whether that takes a second or a year. Depth lives in the companion page on durable execution; this page places it next to the others.
- Apache Airflow is a DAG scheduler. You author pipelines as Python that builds a graph of tasks, and a scheduler runs them โ classically on a time cadence โ with a vast operator ecosystem and a pipeline-ops UI. It re-runs tasks; it does not resume a function mid-line.
- AWS Step Functions is a managed state machine. You describe the workflow as a JSON state machine in Amazon States Language, and AWS runs it for you โ no servers, wired directly into 200+ AWS services.
- Same word, three machines. The deepest split is who owns durability and what the unit of work is: a durable function (Temporal), a scheduled task in a graph (Airflow), or a state in a managed state machine (Step Functions). Almost every other difference follows from that.
Three philosophies
Pick the right axis and the whole comparison snaps into focus. That axis is: what is the unit of work, who guarantees it survives failure, and how do you author it? Each tool answers differently, and almost everything else โ languages, scaling, pricing, lock-in โ is downstream of the answer.
- Temporal โ the durable-execution engine. The unit is a workflow function that calls activities. Durability is owned by the engine: it records an event history and rebuilds in-memory state by replaying the log after any crash. You author in code (Go, Java, Python, TypeScript, .NET, PHP). The pitch: "write code as if failure doesn't exist."
- Airflow โ the DAG scheduler. The unit is a task inside a directed acyclic graph. There is no replay and no resumed call stack: durability is the scheduler re-running tasks whose state lives in a metadata database, on the assumption that tasks are idempotent. You author in Python that declares the graph; the bodies do the work. It is built for batch pipelines on a schedule.
- Step Functions โ the managed state machine. The unit is a state in a finite-state machine. Durability is owned by the managed AWS service, which persists every state transition for you. You author declaratively in Workflow Studio or raw ASL JSON; the real logic lives in the AWS services each state invokes.
The comparison matrix
The exhaustive side-by-side. Use the controls to filter rows down to one dimension or highlight a single tool's column. (Both persist across reloads; Copy as Markdown always exports the full matrix.)
| Dimension | Temporal | Apache Airflow | Step Functions |
|---|---|---|---|
| Category | Durable-execution engine | DAG-based batch scheduler | Managed serverless state machine |
| Core philosophy | Write failure-proof code | Pipelines as code, run on time | Orchestrate AWS with a JSON state machine |
| Authoring model | Imperative code (workflows + activities) | Python DAGs (operators / sensors / TaskFlow) | Declarative ASL JSON or Workflow Studio |
| Unit of work | Workflow + Activity | Task (an operator instance) | State (Task / Choice / Map / Parallel / โฆ) |
| Language support | Go, Java, Python, TypeScript, .NET, PHP | Python (task bodies can shell out to anything) | None โ JSON DSL; logic lives in Lambda/services |
| Durability mechanism | Event-sourced history + deterministic replay | Scheduler re-runs tasks; state in metadata DB | Managed service persists each state transition |
| Execution guarantee | Exactly-once effect via replay + idempotent activities | At-least-once task runs; relies on idempotency | Standard: exactly-once ยท Express: at-least-once |
| Max run duration | Effectively unbounded (years); bounded by history size | No hard per-run cap; tasks bounded by timeouts | Standard: 1 year ยท Express: 5 minutes |
| Long waits / human-in-loop | Durable timers, signals, updates (months, $0 idle) | Sensors / deferrable operators; awkward for live input | Wait state; .waitForTaskToken |
| Triggering | Code / API-driven (start a workflow); cron via Schedules | Time-driven (cron / timetables) + data-driven Assets | Event / API-driven (StartExecution, EventBridge, API Gateway) |
| State & data passing | Plain in-memory variables; activity return values | XCom (small payloads via metadata DB) | JSON in/out via JSONPath/JSONata; variables |
| Error handling / retries | Per-activity retry policies; sagas/compensation in code | Task retries / retry_delay; trigger rules; callbacks | Retry / Catch in ASL; per-state backoff |
| Concurrency & scale | Stateless workers; sharded history; namespaces | Scheduler + Celery/K8s workers; pools/parallelism | Fully managed autoscale; high per-account quotas |
| Architecture | Temporal Service (Frontend/History/Matching) + your workers + DB | Scheduler + webserver + DAG processor + workers + triggerer + DB | Opaque managed service; you see only the API/console |
| Deployment | Self-host OSS or Temporal Cloud | Self-host or managed | Managed-only (AWS) |
| First-party managed | Temporal Cloud | AWS MWAA, Google Cloud Composer | Native AWS service |
| Observability / UI | Temporal Web UI (per-execution event history) | React UI: Grid / Graph / logs | Console graph + Workflow Studio; Express logs โ CloudWatch |
| Versioning | Workflow versioning APIs / Worker Versioning | DAG versioning (a run finishes on its start version) | State-machine versions + aliases |
| Testing / local dev | Time-skipping test framework; local dev server | airflow dags test; unit-test operators; local Docker | TestState API / Step Functions Local; mostly cloud |
| Pricing model | OSS + infra, or Temporal Cloud per-action | OSS + infra; MWAA/Composer per-environment | Standard $0.000025/transition ยท Express $1.00/M req + $0.00001667/GB-s |
| Vendor lock-in | Low (OSS, portable, multi-cloud) | Low (OSS, portable); managed variants add some | High (AWS-only; ASL is AWS-specific) |
| Ecosystem | SDKs + your own activities | Huge provider/operator catalog | 200+ AWS SDK integrations + optimized integrations |
| Best-fit use cases | Long-running stateful logic, sagas, microservice orchestration | ETL/ELT & ML batch pipelines, scheduled jobs | Serverless app orchestration, AWS-native ETL, event glue |
| Sharpest footgun | Determinism constraints on workflow code | It is not durable execution; scheduler semantics | ASL verbosity + deep AWS coupling |
Execution & durability
"What survives a crash?" is the question that separates these three most sharply. They give three genuinely different answers, and confusing them is the most common mistake in this space.
- Temporal replays a log. Every step a workflow takes is appended to a durable event history. When a worker dies mid-execution, a fresh worker re-runs the function from the top, but each already-recorded step returns its stored result instantly instead of re-executing. When replay catches up to the last event, the in-memory state โ locals, call stack, pending
awaits โ is exactly as it was, and execution continues. The function literally resumes mid-line. (This is the subject of the companion page.) - Airflow re-runs tasks. There is no replayed call stack. The scheduler tracks each task's state (
queued,running,success,failed) in the metadata database. If a task or worker dies, the scheduler simply re-queues that task from the start, up to itsretriescount. Durability is therefore an emergent property of idempotent tasks plus the metadata DB, at task granularity โ not a resumed program. - Step Functions persists transitions. The managed service records the result of every state transition. If a downstream service fails, the state machine retries or catches per its ASL rules; the execution itself is never "lost" because AWS holds its state. Standard workflows give exactly-once execution of each state; Express trades that for throughput and is at-least-once.
Airflow is a scheduler, not a durable-execution engine. It does not replay a function or resume a call stack โ it re-runs tasks, and its durability is only as good as your tasks' idempotency plus what is recorded in the metadata DB. If you need a months-long process that resumes exactly where a function blocked, that is Temporal's model (or, within AWS, a Step Functions Standard workflow), not Airflow's.
Scheduling & triggering
How does a run begin? This is one of the most practical differences, and it reflects each tool's heritage.
- Airflow is time-first. Its native instinct is the cadence: a DAG declares a
schedule(a cron string or a timetable) and the scheduler creates a run per interval. Modern Airflow adds data-aware scheduling โ a DAG can run when its upstream Assets update โ and external event triggers, but the batch-interval model is its center of gravity. - Step Functions is event/API-first. An execution starts on an explicit
StartExecutioncall โ directly, from API Gateway, or, most commonly, from EventBridge rules and schedules. It is built to react to events flowing through AWS, with cron available via an EventBridge schedule. - Temporal is code/API-first. A workflow starts when a client calls
start_workflow()from anywhere in your system โ typically in response to a user action or an event. For recurring runs, Temporal Schedules provide cron-like and calendar triggering as a first-class server feature.
Rule of thumb: if you think in "every night at 2am, process yesterday's data," that is Airflow's home turf. If you think in "when this event happens, run this process," Step Functions and Temporal start a run per event rather than per interval.
State, data & retries
Passing data between steps
Because Temporal workflows are ordinary functions, intermediate state is just local variables and activity return values held in memory (and reconstructed on replay). Airflow tasks are separate processes, so values move through XCom, persisted in the metadata DB and meant for small payloads โ bulk data should travel through external storage (S3, a warehouse) with only references in XCom. Step Functions threads a JSON document through the states, reshaped per state with JSONPath or JSONata, with a hard 256 KB payload limit between states (again: keep large data in S3 and pass pointers).
Both Airflow's XCom and Step Functions' state payload are deliberately small. The pattern in both is identical: keep large objects in object storage and pass handles. Temporal activity results can be larger, but the same discipline pays off โ the event history (and thus replay cost) grows with what you record.
Retries & failure
All three express retries declaratively, per step โ but their defaults are opposite, which is easy to miss. Temporal retries every activity automatically by default (unlimited attempts, exponential backoff), so you attach a retry policy to cap it โ or, as in the code above, a maximum_attempts=1 policy to switch it off; it also models all-or-nothing flows as sagas with explicit compensation in code. Airflow and Step Functions are the reverse โ a step does not retry unless you opt in: Airflow via retries/retry_delay (default 0) plus trigger rules and on_failure_callback hooks; Step Functions via Retry and Catch blocks per state, routing caught errors to a fallback. So matching them up takes extra code on the Temporal side (the NO_RETRY policy) and zero on the others โ the opt-out-vs-opt-in difference made literal.
Architecture
What you actually operate is wildly different across the three โ from "a database plus a few stateless services" to "literally nothing."
Airflow: several cooperating components
Airflow self-hosted is a small distributed system around a central metadata database. The scheduler decides what to run and hands tasks to an executor (Local, Celery, or Kubernetes); the DAG processor parses your Python DAG files; workers execute tasks; the triggerer runs deferred/async waits; and a webserver / API server serves the UI and REST API. Everyone coordinates through the metadata DB.
Step Functions: a black box
There is no architecture to run. Step Functions is a fully managed AWS service: you register a state machine and call StartExecution. AWS owns the scheduler, the durable store, the retries, and the scaling. Your "components" are the AWS services each Task state invokes โ Lambda, SQS, SNS, DynamoDB, ECS, and, via SDK integrations, 200+ services and thousands of API actions.
Temporal: a service plus your workers
Temporal sits in between: a Temporal Service (its Frontend, History, and Matching roles, backed by a database such as Cassandra, MySQL, or PostgreSQL) plus your own stateless workers that host the workflow and activity code and long-poll for tasks. You run the cluster (or buy Temporal Cloud) and you run the workers; the durable state lives in the service. The internals get a full treatment on the durable-execution page.
Deployment & managed offerings
This is where lock-in is decided. Two of the three are open source and portable; one is a single cloud.
| Self-host | First-party managed | Portability | |
|---|---|---|---|
| Temporal | Yes โ OSS server (MIT) | Temporal Cloud | High โ same OSS anywhere; multi-cloud |
| Airflow | Yes โ OSS (Apache 2.0) | AWS MWAA ยท Google Cloud Composer ยท (Astronomer, 3rd-party) | High โ real Apache Airflow across vendors |
| Step Functions | No | AWS only (the service itself) | Low โ ASL and integrations are AWS-specific |
For Airflow, note the distinction the marketing pages blur: MWAA and Cloud Composer are managed deployments of the real Apache Airflow โ not forks โ so a DAG can in principle move between them and a self-hosted cluster. Astronomer is the main commercial vendor (and steward of much of the project), but it is third-party rather than a hyperscaler offering. Step Functions has no self-hosted form at all: choosing it is choosing AWS.
Scale & limits
All three scale to serious production load, but they hit different ceilings.
- Temporal scales by adding stateless workers and by sharding workflow histories across the cluster; high-scale users run millions of concurrent executions. The practical limit per workflow is event-history size โ long-running loops use Continue-As-New to start a fresh history and avoid unbounded growth.
- Airflow scales workers horizontally (Celery or Kubernetes) and throttles with pools and parallelism settings. Its historical bottleneck is the scheduler and metadata DB under very high task volume โ a constraint teams operating Airflow at scale have written about candidly (Shopify's account of running thousands of DAGs is a good primary read). It is tuned for throughput of batch work, not low-latency per-event execution.
- Step Functions autoscales transparently. The numbers are AWS account quotas (some soft/adjustable): Standard supports thousands of state transitions per second; Express is built for very high event rates (on the order of 100,000 executions/second). The hard caps that bite are the duration limits โ Standard 1 year, Express 5 minutes โ and the 256 KB payload between states.
Observability & versioning
Watching a run
Each ships a first-party UI tuned to its model. Airflow's React UI is pipeline-ops centric: the Grid and Graph views show task status across runs, with logs per task instance. Step Functions' console renders the state machine graph and animates each execution's path, with Standard executions keeping full history in-console and Express sending logs to CloudWatch. Temporal's Web UI is execution-centric: it shows the complete event history of any single workflow, which is the natural debugging unit for durable execution.
Changing a running workflow
Long-lived workflows must survive their own code changing underneath them. Temporal takes this most seriously because replay makes it hard: workflow versioning (patching) and Worker Versioning let new code coexist with in-flight executions (detailed on the durable-execution page). Airflow provides DAG versioning so a run completes on the DAG version it started with. Step Functions supports versions and aliases of a state machine, so you can publish an immutable version and shift traffic deliberately.
Cost models
Three different shapes of bill, which matter as much as features at scale.
- Temporal โ the OSS server is free; you pay for the infrastructure you run it on (and the operational effort). Temporal Cloud instead bills per action (workflow and activity operations) plus storage, so cost tracks usage rather than fixed servers.
- Airflow โ OSS is free plus infrastructure. Managed Airflow is priced per environment: MWAA and Cloud Composer charge for the running environment (size/uptime) regardless of how busy your DAGs are, so an idle Airflow still costs money.
- Step Functions โ pure pay-per-use, and the two workflow types bill on different axes:
- Standard:
$0.000025per state transition, with 4,000 free transitions/month. Predictable, audit-grade, can run a year. - Express:
$1.00per million requests plus$0.00001667per GB-second of duration. Cheap at very high volume and short durations.
- Standard:
Temporal and self-hosted Airflow are a fixed-ish infrastructure bill that amortizes well under heavy, steady load. Managed Airflow is a per-environment bill that suits always-on pipelines. Step Functions is per-execution, which is unbeatable for spiky, low-volume serverless work and can get expensive for very chatty, high-frequency Standard state machines (watch the transition count).
When to use which
The honest one-liners, then a flow.
- Reach for Temporal when the work is long-lived, stateful application logic that must survive crashes and deploys โ sagas with compensation, multi-day human-in-the-loop, microservice orchestration โ expressed as ordinary code in your language and portable across clouds.
- Reach for Airflow when the work is scheduled batch data pipelines (ETL/ELT/ML) and you want a rich operator ecosystem plus a pipeline-ops UI, and your team thinks in Python and dependency graphs. Not for per-request or durable-app logic.
- Reach for Step Functions when you are all-in on AWS and want to orchestrate AWS services with minimal ops and pay-per-use. Choose Standard for long-running, auditable, exactly-once flows (up to a year); Express for high-volume, short (<5 min), at-least-once event processing.
Common misconceptions
No. Airflow is a scheduler that re-runs tasks; it has no replay and no resumed call stack. Durability is your tasks being idempotent plus the metadata DB. For resume-mid-function semantics, use Temporal (or a Step Functions Standard workflow).
They have different guarantees: Standard is exactly-once and runs up to a year; Express is at-least-once and capped at five minutes. That difference, not just price, dictates which you can use.
ASL is a declarative JSON DSL that orchestrates; it has no general-purpose computation. The actual logic lives in the Lambda functions and AWS services each Task state invokes.
Effectively unbounded, not literally unlimited: a single execution is bounded by its event-history size, which is why long loops call Continue-As-New to reset history.
They are managed deployments of the real Apache Airflow, so DAGs stay portable. The chief commercial vendor, Astronomer, is a third party โ not a hyperscaler service.
Further reading
Temporal
- Durable Execution โ the companion design doc on this site: the full Temporal/Cadence deep-dive โ event history, deterministic replay, the durable toolkit, server internals, and versioning.
- temporal.io and docs.temporal.io โ the primary source for the concepts and SDK examples summarized here.
Apache Airflow
- airflow.apache.org โ the official site and docs (architecture overview, core concepts, executors); current stable is the 3.x line.
- apache/airflow on GitHub โ the source and release notes.
- Apache Airflow โ Wikipedia โ project history and high-level overview.
- Amazon MWAA and Google Cloud Composer โ the first-party managed Airflow services.
- Shopify Engineering โ lessons learned running Airflow at scale and "Airflow's problem" โ practitioner perspectives on Airflow's scaling and design edges. An introduction to Airflow for the core model.
AWS Step Functions
- aws.amazon.com/step-functions and the Developer Guide โ workflow types, ASL, integration patterns, and quotas.
- Step Functions pricing โ the exact Standard and Express figures used above.
- Datadog โ AWS Step Functions knowledge center โ a concise external overview.