AWS Lambda Durable Execution SDK for .NET

July 1, 2026 · View on GitHub

Preview. Amazon.Lambda.DurableExecution is in active development (0.x). Public APIs may change before 1.0.

Amazon.Lambda.DurableExecution is the .NET SDK for building resilient, long-running AWS Lambda functions that automatically checkpoint progress and resume after failures. Workflows can run for up to one year, with charges only for active compute time.

Key Features

  • Automatic checkpointing — progress is saved after each step; failures resume from the last checkpoint.
  • Cost-effective waits — suspend execution for minutes, hours, or days without compute charges.
  • Configurable retries — built-in retry strategies with exponential backoff and jitter.
  • Replay safety — functions deterministically resume from checkpoints after interruptions.
  • Type safety — full generic type support for step results.
  • AOT-friendly — pluggable ILambdaSerializer so you can register SourceGeneratorLambdaJsonSerializer<TContext> for trimmed / Native AOT functions.

How It Works

Your handler delegates to DurableFunction.WrapAsync, which gives your workflow function an IDurableContext. The context is your interface to durable operations:

  • ctx.StepAsync — run code and checkpoint the result. (docs)
  • ctx.WaitAsync — suspend execution without compute charges. (docs)
  • ctx.WaitForConditionAsync — poll a check function until a condition is met, suspending between polls. (docs)
  • ctx.CreateCallbackAsync / ctx.WaitForCallbackAsync — wait for external events (approvals, webhooks). (docs)
  • ctx.RunInChildContextAsync — run an isolated child context with its own checkpoint log. (docs)
  • ctx.ParallelAsync — run independent branches concurrently and aggregate their results. (docs)
  • Every user Func receives a CancellationToken linking the caller's token with the SDK's workflow-shutdown signal. (docs)

Quick Start

Installation

dotnet add package Amazon.Lambda.DurableExecution

Your first durable function

Programming model: durable functions support both the executable programming model (shown below) and the class-library programming model on the managed dotnet10 runtime. See the class-library variant below.

A complete order-processing workflow with two steps and a wait, deployed as an executable assembly on the dotnet10 runtime. Main builds a LambdaBootstrap with your handler and an ILambdaSerializer, and DurableFunction.WrapAsync uses that serializer to checkpoint step inputs and outputs.

using Amazon.Lambda.Core;
using Amazon.Lambda.DurableExecution;
using Amazon.Lambda.RuntimeSupport;
using Amazon.Lambda.Serialization.SystemTextJson;

namespace OrderProcessor;

public class OrderProcessor
{
    public static async Task Main()
    {
        var handler = new OrderProcessor();
        var serializer = new DefaultLambdaJsonSerializer();
        using var wrapper = HandlerWrapper.GetHandlerWrapper<DurableExecutionInvocationInput, DurableExecutionInvocationOutput>(
            handler.Handler, serializer);
        using var bootstrap = new LambdaBootstrap(wrapper);
        await bootstrap.RunAsync();
    }

    public Task<DurableExecutionInvocationOutput> Handler(
        DurableExecutionInvocationInput input, ILambdaContext context)
        => DurableFunction.WrapAsync<Order, OrderResult>(Workflow, input, context);

    private async Task<OrderResult> Workflow(Order order, IDurableContext ctx)
    {
        var reservation = await ctx.StepAsync(
            async (_, ct) => await InventoryService.ReserveAsync(order.Items, ct),
            name: "reserve-inventory");

        var payment = await ctx.StepAsync(
            async (_, ct) => await PaymentService.ChargeAsync(order.PaymentMethod, order.Total, ct),
            name: "process-payment");

        await ctx.WaitAsync(TimeSpan.FromHours(2), name: "warehouse-processing");

        var shipment = await ctx.StepAsync(
            async (_, ct) => await ShippingService.ShipAsync(reservation, order.Address, ct),
            name: "confirm-shipment");

        return new OrderResult(order.Id, shipment.TrackingNumber);
    }
}

public record Order(string Id, IReadOnlyList<OrderItem> Items, PaymentMethod PaymentMethod, decimal Total, Address Address);
public record OrderResult(string OrderId, string TrackingNumber);

For AOT or trim-friendly serialization, swap DefaultLambdaJsonSerializer for SourceGeneratorLambdaJsonSerializer<TContext> and register your JsonSerializerContext.

Class-library programming model

On the managed dotnet10 runtime you can skip the Main/LambdaBootstrap loop entirely and deploy a plain class-library handler — the same model used by non-durable Lambda functions. Declare the serializer with an assembly attribute and deploy with the Assembly::Namespace.Type::Method handler string (e.g. OrderProcessor::OrderProcessor.OrderProcessor::Handler):

using Amazon.Lambda.Core;
using Amazon.Lambda.DurableExecution;
using Amazon.Lambda.Serialization.SystemTextJson;

[assembly: LambdaSerializer(typeof(DefaultLambdaJsonSerializer))]

namespace OrderProcessor;

public class OrderProcessor
{
    public Task<DurableExecutionInvocationOutput> Handler(
        DurableExecutionInvocationInput input, ILambdaContext context)
        => DurableFunction.WrapAsync<Order, OrderResult>(Workflow, input, context);

    private async Task<OrderResult> Workflow(Order order, IDurableContext ctx)
    {
        // ...same workflow body as above...
    }
}

The project is a normal Lambda class library; the managed runtime supplies the bootstrap loop and invokes Handler directly.

Using Lambda Annotations

If you use Amazon.Lambda.Annotations, you can skip the handler/WrapAsync boilerplate. Annotate your workflow method with both [LambdaFunction] and [DurableExecution] — the source generator emits the handler wrapper that calls DurableFunction.WrapAsync, and adds the DurableConfig block and checkpoint-API IAM permissions to the generated serverless.template.

using Amazon.Lambda.Annotations;
using Amazon.Lambda.DurableExecution;

public class OrderProcessor
{
    [LambdaFunction]
    [DurableExecution(executionTimeout: 300, RetentionPeriodInDays = 7)]
    public async Task<OrderResult> Workflow(Order order, IDurableContext ctx)
    {
        var reservation = await ctx.StepAsync(
            async (_, ct) => await InventoryService.ReserveAsync(order.Items, ct),
            name: "reserve-inventory");
        // ... remaining steps ...
        return new OrderResult(order.Id, /* ... */ "tracking");
    }
}

Works with both the executable and class-library programming models — set [assembly: LambdaGlobalProperties(GenerateMain = true)] for the executable model, or omit it for a class library (the generated serverless.template Handler adapts to each). The generator validates the (TInput, IDurableContext) -> Task/Task<TOutput> signature and Zip packaging, and reports a diagnostic otherwise.

Documentation

Core operations

  • Steps — execute code with automatic checkpointing, retry strategies, and at-least/at-most-once semantics.
  • Wait — pause execution without compute charges.
  • Wait For Condition — poll until a condition is met, suspending between polls with a configurable wait strategy.
  • Callbacks — wait for external systems to respond.
  • Child Contexts — group related operations into isolated, checkpointed units.
  • Parallel — fan out independent branches concurrently with configurable concurrency and completion policies.

Examples

End-to-end test functions (each paired with an integration test) live under Libraries/test/Amazon.Lambda.DurableExecution.IntegrationTests/TestFunctions/.