# Durable functions or Step Functions?

> A job has several steps, retries and waits. Do I write it as a Lambda durable function, or as a Step Functions state machine? Durable functions for application logic: the workflow is ordinary code in one Lambda, each risky call a step with its own retries, and about half the price of Step Functions Standard for the same job. Step Functions when the workflow is mostly AWS services talking to each other, must be read as a diagram by people who don't read code, needs exactly-once steps for payments, or fans out over millions of S3 objects. Both when a state machine already orchestrates the services and one of its steps is a multi-step job: that step becomes a durable function.

- A choice between tools from The Stack by Nicolas Dao (Cloudless Labs, Sydney): when to pick each, and when to run both
- Last reviewed: 27 October 2026, by Nicolas Dao
- Web page: https://cloudlesslabs.com/stack/guides/durable-or-step-functions/. The whole stack in one file: https://cloudlesslabs.com/stack/brief.md
- Tools: [Lambda durable functions](https://cloudlesslabs.com/stack/aws-lambda-durable-functions/index.md) (Watching), [AWS Step Functions](https://cloudlesslabs.com/stack/aws-step-functions/index.md) (Never), [AWS Lambda](https://cloudlesslabs.com/stack/aws-lambda/index.md) (My default), [Amazon SQS](https://cloudlesslabs.com/stack/aws-sqs/index.md) (My default), [Amazon EventBridge](https://cloudlesslabs.com/stack/aws-eventbridge/index.md) (My default)

## When to pick which

Pick Lambda durable functions when:

- The steps are your own code calling APIs, models and Postgres: the logic stays in the repository, where a developer or an agent reads and tests it.
- A failure in step three mustn't rerun steps one and two, and each step needs its own backoff.
- Chained model calls, where a rerun pays again for tokens already spent.
- Waits for a person or an outside system, for hours or days, with no compute charged while it waits.

Pick Step Functions when:

- The workflow is mostly AWS services calling each other (S3, DynamoDB, Glue, ECS, SNS): over 220 of them connect directly, with no Lambda in between.
- People who don't read code must review or sign off the flow: the graph in the console is the documentation.
- A step must run exactly once, such as a payment: Standard workflows don't repeat a step unless you ask them to retry.
- A batch fans out over millions of S3 objects: Distributed Map runs up to 10,000 child workflows at a time.

Pick SQS + Lambda alone when:

- One step, where rerunning the whole handler is harmless: neither product earns its place. See SQS retries or Durable Functions?

Both, when:

- A state machine that already orchestrates AWS services keeps doing that, and the step that is really a multi-step job in code becomes one durable function it invokes. AWS's own guidance describes this split.
- Not the same logic in both: a retry policy in the state machine and another in the function retry each other, and a failure costs twice.

Questions that change the answer:

- Is most of the work your own code, or AWS services calling each other? Code points to durable functions, services to Step Functions.
- Who has to read the workflow? Developers and agents read code; an auditor or a business owner reads a diagram.
- Must any step run exactly once? Standard workflows give that; a durable step runs at least once per attempt, so it needs an idempotency key.
- Does a single run need more than 30,000 steps or 25,000 history events? Both cap a run, so long loops become child runs in either.

## The short version

| | Lambda durable functions | Step Functions Standard | Step Functions Express |
|---|---|---|---|
| My verdict | Watching: what I'd pilot | Never, for my own builds | Never, for my own builds |
| The workflow is | Sequential code in one Lambda (Node.js, Python, Java, .NET) | A state machine in JSON or YAML (Amazon States Language) | The same, for short, high-volume runs |
| Longest run | 1 year (asynchronous); 15 minutes behind an SQS trigger | 1 year | 5 minutes |
| Size of one run | 30,000 operations, 1 MB per checkpoint, 100 MB in all | 25,000 history events, 256 KB per state | 256 KB per state |
| A step runs | At least once per attempt | Exactly once, unless you retry it | At least once |
| Retries | Per step, in code: backoff, jitter, which errors | Per state, in the definition | Per state, in the definition |
| Waits | No compute charged while waiting | Free while waiting, per transition | Billed by duration |
| AWS services without a Lambda | No: each call is your code | 220+ services directly | 220+ services directly |
| Debugging | Your logs, traces and tests, plus the execution history | The graph of every execution, in the console | CloudWatch Logs |
| 1M four-step runs a month, 10% retried once (Sydney) | about $53 | about $110 | about $10 |

The prices count the orchestration plus the same Lambda compute (512 MB, 200 ms a step). Express is the cheapest line, but it stops at 5 minutes, runs steps at least once and has no callbacks or redrive, so it's for short pipelines that can repeat, not for the jobs this guide is about.

## Durable functions: the workflow is the code

You write the job as an ordinary function. Each call that can fail goes in `context.step()` with its own retry strategy; `context.wait()` and `waitForCallback()` pause it for minutes or months. Lambda saves the result of every completed step. When step three fails, the execution suspends, waits out the backoff without compute charges, and replays the function from the top, reading steps one and two from their saved results instead of running them again.

What that buys is one program. The branches, the retries and the business rules sit in the repository next to the rest of the app, under the same tests and the same review. An agent like Claude Code reads it, changes it and runs its tests like any other code. That's the main reason I'd pick it.

What it costs, in Sydney: $8.70 per million durable operations (the start, each completed step, each wait, each retry), $0.28 per GB written, $0.17 per GB-month kept, and normal Lambda requests and compute, replays included. For the month in the table: 5.1 million operations ($44.37), 4.75 GB written ($1.33), history kept 14 days ($0.37), and about $7 of Lambda.

Its rules are the ones that keep it on my watch list. Code outside a step runs again on every replay, so clocks, random IDs and reads go inside steps. A step runs at least once per attempt, so outside calls carry an idempotency key. Renaming a step breaks the executions in flight. And durable mode can only be switched on when the function is created. The details are on the [durable functions entry](https://cloudlesslabs.com/stack/aws-lambda-durable-functions/index.md).

## Step Functions: the workflow is a document

[Step Functions](https://cloudlesslabs.com/stack/aws-step-functions/index.md) runs a state machine you describe in Amazon States Language, and draws every execution as a graph. It's genuinely good at what it was built for. Standard workflows run a step exactly once unless you tell them to retry, which matters when a step moves money. Over 220 AWS services connect directly, so a step can write DynamoDB, start an ECS task or run a Glue job with no Lambda at all. A callback token pauses a run until something outside answers, redrive restarts a failed run from the step that failed, and Distributed Map fans out over millions of S3 objects.

What it costs, in Sydney: $0.025 per 1,000 state transitions on Standard (the same as us-east-1), with 4,000 free each month. Every retry is another transition. For the month in the table: 4.1 million transitions, $102.40, plus the same $7.65 of Lambda. Express is $1 per million runs plus duration.

Why it's Never for my own builds: the cost is in complexity, not dollars. The business logic moves out of the code into a document with its own language, its own limits (256 KB per state, 25,000 events per run) and its own debugging console. A developer or an agent working on the app now has two programs to understand and a seam between them. In Sydney, Standard workflows are also throttled at 800 state transitions a second by default, against 5,000 in the largest US and EU Regions.

## When each one is right

AWS's own guidance draws the line where I would: durable functions for Lambda-centred application code, written and tested in an IDE; Step Functions for workflows that span many AWS services, need a visual design, or must be signed off by people who don't write code. It also suggests starting with durable functions and adding Step Functions only when the orchestration of services calls for it.

So the questions are practical. If the steps are your code calling APIs, models and a database, it's a durable function. If they're AWS services handing work to each other, or the diagram itself is a deliverable, it's Step Functions. If there's only one step, it's neither: SQS in front of a plain Lambda, as in [SQS retries or Durable Functions?](https://cloudlesslabs.com/stack/guides/retries/index.md).

## Both, without paying twice

When a client already runs a state machine that works, I leave it alone. If one of its steps has grown into a multi-step job of its own, with several outside calls and retries, that step becomes a durable function the state machine invokes. The state machine keeps orchestrating the services; the function keeps its logic in code.

The one thing not to do is retry the same work in both. A Retry block in the state machine around a durable function that retries its own steps multiplies the attempts, and the bill, on every failure. Retry inside the function, and let the state machine handle only the function's final failure.

Sources, checked on 27 October 2026: [Lambda durable functions](https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html), [durable functions or Step Functions (AWS's guidance)](https://docs.aws.amazon.com/lambda/latest/dg/durable-step-functions.html), [durable functions quotas](https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html#durable-functions-quotas), [the October 2026 limit increase](https://aws.amazon.com/about-aws/whats-new/2026/10/aws-lambda-durable-gen-limit-increase/), [Lambda pricing](https://aws.amazon.com/lambda/pricing/), [Step Functions pricing](https://aws.amazon.com/step-functions/pricing/), [Standard vs Express workflows](https://docs.aws.amazon.com/step-functions/latest/dg/choosing-workflow-type.html), [Step Functions quotas](https://docs.aws.amazon.com/step-functions/latest/dg/service-quotas.html), [Step Functions redrive](https://aws.amazon.com/blogs/compute/introducing-aws-step-functions-redrive-a-new-way-to-restart-workflows/).

## Related

- [Lambda durable functions](https://cloudlesslabs.com/stack/aws-lambda-durable-functions/index.md): the full entry (verdict, pricing, gotchas)
- [AWS Step Functions](https://cloudlesslabs.com/stack/aws-step-functions/index.md): the full entry (verdict, pricing, gotchas)
- [AWS Lambda](https://cloudlesslabs.com/stack/aws-lambda/index.md): the full entry (verdict, pricing, gotchas)
- [Amazon SQS](https://cloudlesslabs.com/stack/aws-sqs/index.md): the full entry (verdict, pricing, gotchas)
- [Amazon EventBridge](https://cloudlesslabs.com/stack/aws-eventbridge/index.md): the full entry (verdict, pricing, gotchas)
- [Every decision guide, the choices between tools first](https://cloudlesslabs.com/stack/guides/index.md)
- [Map of every Markdown file on cloudlesslabs.com, with what each one answers](https://ask.cloudlesslabs.com)
