Durable Workflow Engines in 2026: Every Major Option Compared
This is a comparison of the major durable workflow engines of 2026, the tools for multi-step processes with retries, long delays, and state that survives crashes: Upstash Workflow, Temporal, Inngest, Trigger.dev, AWS Step Functions, and Cloudflare Workflows. For single-message background jobs and queues, see our companion comparison.
Upstash Workflow stands out on four things:
- Zero infrastructure on both sides. Steps run inside your existing app as HTTP calls; Upstash holds the state. No workers, no server, no Postgres to operate.
- Idle time is free. A workflow can sleep for a month or wait on an event with no compute running anywhere, and
context.callexecutes long HTTP requests (up to 12 hours) on Upstash's side, not yours. - Failure handling with resume. Automatic per-step retries, then a DLQ where you can resume a run from the failed step after shipping a fix, without losing completed steps.
- Per-step pricing. $1 per 100K steps. The bill scales to zero, and there is no monthly platform fee to grow into.
The moment a background job becomes a chain (call the model, wait for a webhook, sleep 30 days, then charge the card) you have a new problem. Any single step can fail or the process can die between steps, and you need the whole thing to pick up where it left off. That's durable execution, and in 2026 there are at least six serious tools selling it.
They differ less in features (everyone has retries, sleeps, and events) than in architecture: where your steps run, and what you have to operate to keep them running. That decides your infrastructure bill, your deploy story, and whether the tool works from a serverless app at all. So that's how this comparison is organized.
I checked every claim below against the providers' docs and pricing pages in July 2026. These change often, so confirm the current state before you commit.
Every major engine at a glance
| Engine | Where your steps run | What you operate | Max sleep | Cloud entry price | Metered on |
|---|---|---|---|---|---|
| Upstash Workflow | Your HTTP routes, one call per step | Nothing | 1 year PAYG, unlimited Fixed | $0, pay-as-you-go | Steps, $1 per 100K |
| Inngest | Your app, invoked over HTTP | Nothing (cloud) or self-host server | 1 year (7 days free) | Free tier, then $99/mo Pro | Executions |
| Trigger.dev | Their managed workers | Nothing (cloud) or self-host | No documented cap | $0 plus usage | Compute-seconds + per-run fee |
| Temporal | Workers you run | Worker fleet, plus server + DB if self-hosted | Years | Greater of $100/mo or 5% of usage | Actions + storage |
| AWS Step Functions | Lambda / AWS services, wired in JSON | Nothing (AWS) | 1 year (max execution) | 4,000 transitions/mo free | State transitions, $25/M |
| Cloudflare Workflows | Your Cloudflare Worker | Nothing (Cloudflare) | 365 days | Free tier; $5/mo Workers Paid | Requests + CPU + steps |
The deepest split is the middle column. One of them, Temporal, assumes a long-running worker fleet you deploy and keep alive. Three (Step Functions, Cloudflare Workflows, Trigger.dev) run your steps on a specific platform's compute. Two, Inngest and Upstash Workflow, invoke steps inside the app you already have, over HTTP, which is the only model that drops into a serverless codebase with nothing new to run.
Who runs the steps, and who holds the state?
The worker-fleet school. Temporal is the reference point for durable execution, and its docs are precise about the division of labor: the Temporal Service "doesn't execute any of your code". You write workers that poll task queues, and you run them. That stays true on Temporal Cloud: "You run Workers that execute your Workflow and Activity code," deployed on your Kubernetes, VMs, or on-premises. Workflow code must also be deterministic (no direct system time, randomness, or I/O outside activities) because durability comes from replaying event history. In exchange you get the most battle-tested engine here, seven official languages (Go, Java, TypeScript, Python, .NET, PHP, Ruby), and workflows that run for years.
The platform school. AWS Step Functions is a state machine you define in JSON, not code, wiring together Lambda and other AWS services; it's the oldest and most operationally boring option if you live on AWS. Cloudflare Workflows is durable execution built into Workers: real TypeScript with step.do and step.sleep, but only on Cloudflare's runtime. Trigger.dev deploys your TypeScript tasks to its own managed machines, which is why it has no platform timeout and why it appeared in our background jobs comparison too.
The HTTP school. Inngest and Upstash Workflow both start from the same observation: if each step is delivered to your app as a separate HTTP request, and the engine memoizes completed step results, then a workflow can be durable while running entirely inside a serverless function. Your app crashes or redeploys mid-run; the engine re-invokes, completed steps are skipped, and execution resumes exactly where it stopped. Upstash Workflow builds this on QStash, and a workflow is just a route in your app:
import { serve } from "@upstash/workflow/nextjs";
export const { POST } = serve<{ userId: string }>(async (context) => {
const { userId } = context.requestPayload;
const user = await context.run("create-user", () => createUser(userId));
await context.sleep("trial-period", "30d"); // no compute runs for 30 days
const usage = await context.run("check-usage", () => getUsage(userId));
if (usage.active) {
await context.run("start-subscription", () => startSubscription(userId));
}
});Deploy it like any other route, on any of the supported platforms: Next.js, Cloudflare Workers, Hono, Express, Astro, SvelteKit, Nuxt, and more in TypeScript, plus FastAPI and Flask in Python.
What does a sleeping workflow cost?
Most workflows spend most of their life waiting: for a timer, a webhook, a slow API. The engines treat that time very differently.
On Upstash Workflow, a sleep ends the current request entirely; a new one is scheduled when the delay expires, so a 30-day sleep consumes zero compute and bills as one step. The same applies to context.waitForEvent, and to the rarer context.call: an HTTP request to a third-party API that Upstash executes on your behalf, with response windows up to 12 hours. A 40-minute LLM generation doesn't occupy your Vercel function for 40 minutes; your function isn't running at all.
Inngest works the same way for sleeps (up to a year, and sleeping runs don't count against concurrency), though regular steps still execute inside your functions, subject to your platform's duration limits. Cloudflare Workflows doesn't bill idle time either. Trigger.dev checkpoints waits longer than 5 seconds on its cloud so you don't pay compute for them, a feature its self-hosted version lacks. On the worker-fleet side the timers themselves are cheap, but the economics invert: your Temporal workers are running 24/7 whether any workflow is active or not, so idle costs whatever your idle infrastructure costs.
How long can a workflow live, and how big can it get?
The ceilings differ by orders of magnitude, and they're the kind of thing you discover in production:
- AWS Step Functions: Standard executions max out at 1 year, 25,000 history events, and, the one that bites, 256 KiB payloads per state. Express workflows cap at 5 minutes.
- Temporal: event history is limited to 51,200 events or 50 MB per execution, with payloads capped at 2 MB. Long-lived workflows are expected to checkpoint themselves with Continue-As-New — the docs themselves note long histories "may bog down and have performance issues."
- Cloudflare Workflows: sleeps up to 365 days, 1,024 steps per run on the free plan (up to 25,000 on paid), 1 MiB step outputs.
- Inngest: 1,000 steps per run, run length from 30 days (free) to 366 days (Pro), 4 MiB step outputs.
- Upstash Workflow: sleeps up to a year on pay-as-you-go and unlimited on fixed plans; a run caps at 1,000 steps and 100 MB of accumulated state by default (both are plan-level quotas that can be raised); message payloads go from 1 MB (free) to 50 MB (fixed plans).
What happens when a step fails?
Everyone retries; the defaults and the aftermath differ. Temporal retries activities indefinitely by default (workflows themselves don't retry). Cloudflare Workflows defaults to 5 retries per step with exponential backoff; Step Functions ships 3 attempts per Retry block; Inngest 4 retries with an onFailure handler; Trigger.dev 3 attempts with dashboard bulk-replay.
Upstash Workflow retries each step 3 times by default with exponential backoff, and a run that exhausts retries lands in a DLQ with three options: resume from the failed step (completed steps keep their results), restart from scratch, or re-fire the failure callback. A failureFunction in your code receives the error details; the failureUrl variant works even when your whole app is down. Resume-from-failure is the practical difference: ship the fix, resume the run, and the 14 steps that already succeeded don't re-execute.
Waiting for the outside world
Human approvals and webhook callbacks are where workflow engines earn their keep, and every engine has an answer: Temporal has signals and updates, Inngest matches events with CEL expressions, Step Functions pauses on a task token (Standard workflows only), Cloudflare buffers events for step.waitForEvent up to a year, and Trigger.dev waits on tokens.
Upstash Workflow's version is context.waitForEvent plus context.notify: the run suspends and consumes nothing until your code, running anywhere, fires the event, with a configurable timeout. Notifications sent before the workflow reaches the wait are stored and delivered when it gets there, which closes the classic race condition. For third-party webhook providers, context.createWebhook mints a URL you can hand to Stripe or Clerk and wait on directly.
Can it run in a Next.js app on Vercel?
For a serverless codebase, this is the first filter, so here's the honest map:
- Natively, yes: Upstash Workflow and Inngest, which are both designed as routes in your app. On Vercel, Inngest steps run inside your functions (up to the 800-second fluid-compute ceiling with streaming); Upstash Workflow's per-step ceiling is the plan's HTTP response duration (15 minutes free, up to 12 hours on fixed plans), and long third-party calls can be offloaded to
context.callentirely. - Alongside, not inside: Trigger.dev tasks deploy to Trigger's machines with a separate CLI deploy; your Vercel app just triggers them.
- Different platform: Cloudflare Workflows requires your code on Workers; Step Functions orchestrates AWS services, and its direct HTTP task times out at 60 seconds.
- Long-running workers required: Temporal. Its serverless-workers option is pre-release, AWS-Lambda-only, and gated behind a support ticket.
