Step Functions has been the default answer for multi-step serverless workflows for years. At re:Invent 2025 AWS added another option: Lambda durable functions, which let you write a long-running, multi-step workflow as ordinary code inside a Lambda function, with automatic checkpointing and replay. This tutorial covers how the model works, when to choose it over Step Functions, and how to deploy one with the Serverless Framework. Durable function support is new and moving quickly, so verify property names and SDK APIs against the current AWS and Serverless Framework docs before shipping.
The mental model: checkpoint and replay
A durable function is a normal Lambda function with durable execution enabled. Your handler is wrapped by an SDK that records the result of each step to a checkpoint log. If the function is interrupted, because it is waiting, timed out or failed, Lambda invokes it again and replays the handler from the top. Completed steps return their stored results instead of running again, and execution resumes at the first unfinished step.
Two consequences shape how you write the code:
- Code outside a step must be deterministic. It runs again on every replay. Random numbers,
Date.now()and unordered iteration belong inside steps, where the result is recorded once. - Steps should still be idempotent. A step can be retried after a crash that happens before its checkpoint is saved, so side effects such as charging a card need idempotency keys, as covered in our EventBridge, SQS and idempotency tutorial.
Waits are the headline feature. A durable function can pause for a timer or an external callback without keeping compute running, so you are not billed for idle time while it waits.
When to pick durable functions, and when not to
| Situation | Better fit |
|---|---|
| Workflow logic is mostly application code: branching, loops, calling SDKs, with unit-testable logic | Durable functions |
| A small team that wants one language, one repo and normal debugging | Durable functions |
| You need a visual execution graph for ops or auditors | Step Functions |
| You orchestrate many AWS services directly through SDK integrations with no Lambda code | Step Functions |
| Workflows are long-lived, high-volume and already standardized on ASL | Step Functions (keep it) |
We rarely recommend migrating a working state machine. The sweet spot is new workflows where writing ASL would be more complicated than the business logic: approval flows, multi-step onboarding, saga-style payment and fulfilment, and AI agent loops that wait on humans or tools.
Step 1: Define the function in serverless.yml
Durable execution is enabled per function. At the time of writing the Serverless Framework exposes this through a durableConfig block on the function. Confirm the exact keys in the current docs.
service: order-workflow
provider:
name: aws
runtime: nodejs22.x
region: us-east-1
functions:
processOrder:
handler: src/processOrder.handler
timeout: 60 # per-invocation limit, not the workflow limit
durableConfig:
executionTimeout: 86400 # whole workflow may run up to 24h
retentionPeriodInDays: 14 # how long checkpoint history is kept
environment:
ORDERS_TABLE: ${self:service}-${sls:stage}-orders
Note the two different clocks. The normal Lambda timeout limits each individual invocation. The durable execution timeout limits the whole workflow across invocations and replays.
Durable functions are invoked against a qualified ARN, meaning a published version or alias, so that replays always run the same code the execution started with. Don't point callers at $LATEST for production workflows. Publish versions with each deploy and invoke through an alias.
Step 2: Write the handler as steps
The SDK shape below is illustrative. Check the current @aws/durable-execution-sdk-js documentation for exact names.
import { withDurableExecution } from '@aws/durable-execution-sdk-js';
export const handler = withDurableExecution(async (event, context) => {
const { orderId } = event;
// Each step's result is checkpointed. On replay it is returned, not re-run.
const reservation = await context.step('reserve-inventory', async () =>
reserveInventory(orderId)
);
const payment = await context.step('charge-card', async () =>
chargeCard(orderId, { idempotencyKey: `order-${orderId}` })
);
// Pause without paying for compute. Resumes after the delay.
await context.wait({ seconds: 3600 });
const shipment = await context.step('create-shipment', async () =>
createShipment(orderId, reservation)
);
return { orderId, paymentId: payment.id, shipmentId: shipment.id };
});
Keep the code between steps boring: no clocks, no random IDs, no reads from mutable state. If you need a timestamp, take it inside a step.
Step 3: Add retries and compensation
Give each step its own retry policy instead of one blanket policy, because a flaky third-party API and a database write fail differently. For saga-style flows, wrap the risky step in a try/catch and run compensating steps, such as refunding a payment and releasing inventory, as ordinary steps in the catch block. They are checkpointed like any other step, so a crash halfway through a rollback resumes where it stopped.
Step 4: Wait for an external signal
Human approval and webhook-driven flows are where durable functions pay off. The pattern is to create a callback, hand its ID to the outside world, and suspend:
- Inside a step, store the callback ID somewhere the approver can reach, such as a DynamoDB row, or put it in an email link that points to an API route.
- Suspend on the callback with a timeout.
- A second Lambda behind API Gateway receives the approval and completes the callback using the AWS SDK.
Always set a timeout and handle it explicitly. An approval that never arrives should escalate or cancel, not sit forever.
Step 5: IAM, deployment and invocation
Grant the function's role the permissions it needs for its own work, and the durable-execution permissions the docs list for checkpointing. Keep both scoped to the function, as described in our least-privilege IAM tutorial. Then deploy and start an execution through the alias:
serverless deploy --stage dev
aws lambda invoke \
--function-name order-workflow-dev-processOrder:live \
--invocation-type Event \
--cli-binary-format raw-in-base64-out \
--payload '{"orderId":"o-1001"}' out.json
Use asynchronous invocation for anything that outlives a single request. Callers get an acknowledgement, and the workflow continues in the background.
Testing and observability
- Unit-test steps as plain functions. Keep business logic in modules that the step callbacks call, so tests don't need the durable runtime.
- Test replay behavior deliberately. Run the handler twice against the same recorded checkpoint log and assert that you get the same result and no duplicate side effects. AWS provides a testing SDK for this, so check its current state.
- Log with the execution ID. Include it in every structured log line so you can follow one workflow across replays. Powertools for AWS Lambda works well for this.
- Alarm on stuck executions. Track executions that exceed their expected duration, plus failed and timed-out counts. A workflow stalled at a wait is invisible unless you look for it.
Pitfalls we see in reviews
- Non-deterministic code outside steps. This is the most common bug. It causes confusing replay failures that only appear after a wait.
- Large payloads in checkpoints. Pass references, such as an S3 key or a record ID, between steps instead of whole documents.
- Code changes during a running execution. Without versioned invocation, a replay can run new code against old history. Use versions and aliases.
- Treating it as free orchestration. You still pay for invocations, replays and checkpoint storage. Model cost with realistic step counts before moving a high-volume workflow.
- Skipping idempotency because "it only runs once". Retries and replays make at-least-once behavior the default.
Need help choosing an orchestration model?
Picking between durable functions, Step Functions and plain queues is an architecture decision that affects cost, observability and team workflow for years. SleekDeploy's consultants run short design reviews for exactly this. Get in touch with the workflow you are planning.