Suppose a team builds customer support automation for an online store, and one morning they have to explain something awkward to the finance lead. Their agent reaches step seven, the refund call, and the network drops the connection mid-request. The container restarts, the queue redelivers the message, and the agent begins again at step one: read the ticket, classify the intent, look up the order, draft a reply, call the refund endpoint. The refund endpoint is now being called for the second time. The customer gets paid twice, and accounting notices during reconciliation.
The first diagnosis is almost always the model. Swap in something larger, rewrite the prompt in finer detail, wrap the whole thing in retries, and a week later the same double refund shows up in the ledger. The reasoning was never the weak part. Nothing in the system recorded what had already happened by the time the agent reached step seven, so when the process died, that knowledge died with it. A new process starts with an empty head.
Durable execution exists to close that gap. Each step writes its inputs, outputs and state to storage, and after a crash, a machine reboot or a rolling deploy, the next run resumes from the last completed step instead of from the first line of code. It sounds like an infrastructure detail until you count steps. An agent that runs three steps can afford to start over. An agent that runs thirty, with a payment, an email and a database write somewhere in the middle, turns every restart into an incident with a customer attached to it.
One distinction is worth separating early, because teams blur it and then blame the engine. Durability keeps progress from being lost. Idempotency keeps side effects from being repeated. Both are required, and neither substitutes for the other. When the refund call at step seven times out with no response, nobody knows whether the money moved. The engine can retry the step, and retrying is all it can do. What prevents the second charge is the idempotency key you pass to the payment gateway, which lets the gateway recognize the repeat and collapse it into the first request. All four engines discuss retries and recovery in their documentation to varying degrees. None of them can invent that key for you, and none of them knows what counts as “the same operation” in your business.
Four implementations are worth comparing today, from Conductor, Temporal, Inngest and Restate. All four let a flow continue after a crash. They disagree, sharply, about what a flow is. One treats a flow as data that can be reviewed and versioned. One treats it as code that must be replayed. One wraps a layer around functions you already wrote. One puts the entire runtime in the path of your calls. Nearly every selection argument reduces to that question, so it is the right place to start.
Flows as a graph you can version
Conductor takes the least code-shaped route of the four. A workflow is a JSON document. Tasks, branches, loops, parallel paths and subflows all live in the document, while business logic lives in workers, written in Java, Python, Go, JavaScript, C# or another supported language. The engine handles scheduling and persistence. Workers execute and report back.
Consider a compliance approval chain at a cross-border payments company. That chain changes almost every month. Legal adds a review node, risk adjusts a threshold condition, a new jurisdiction needs an extra sign-off. If the chain is code, every one of those edits means a release, a regression run and a deploy window. If the chain is JSON, the edit is a new version of the document pushed to the engine, and the workers stay exactly where they are. The people who want the change and the people who ship the change stop being the same bottleneck.
Many teams frown the first time they hear “JSON workflow.” The word suggests a low-code builder, a drag-and-drop canvas, and an expressiveness ceiling that shows up six weeks into the project. Conductor’s documentation addresses the objection head-on: it is not a low-code platform. Workers are ordinary code and can use any library and any I/O the language offers. JSON only orchestrates. SWITCH, DO_WHILE, FORK_JOIN, SUB_WORKFLOW and DYNAMIC nest inside one another, so a loop can contain a branch, a branch can contain a parallel fork, and a fork can contain a subflow. Complex flows are writable. The cost is that expressions grow long. ${...} sits inside ${...}, and reading a definition is less pleasant than reading code. Debugging happens through the execution tree in the UI, not through a stack trace.
Conductor’s workers pull. A worker asks the server for tasks, executes them, and reports results. The advantage is placement: workers can run anywhere, the server and the worker fleet scale on separate curves, and neither side needs to discover the other. The disadvantage is idle polling, which burns a little capacity when volume is low, so long-poll intervals need tuning. The documentation also covers task domains, rate limits and concurrency caps, which exist so several teams can share one engine without stepping on each other.
The license is the cleanest of the four. Apache 2.0, fully open source, self-hosted. The docs claim support for five persistence backends and six message queues, which means it runs anywhere Docker runs. The project started at Netflix and is now maintained mainly by Orkes, which sells an enterprise cloud on top. Read that arrangement backwards and it still makes sense: the open source edition is complete, and the commercial edition sells hosting plus the surrounding services. For a team that does not want to be locked to a vendor, this is the easiest license to live with.
Code as the workflow, with determinism as the bill
Temporal assumes the opposite of Conductor. A flow is code. You write it in Go, Java, Python, TypeScript, .NET, PHP, Ruby or Rust as a Workflow, you put side effects into Activities, and Workers execute the result. The server is MIT licensed and can be self-hosted, or you can buy Temporal Cloud.
The ceiling on this route is high. Take order fulfillment that runs for weeks, where the address may change halfway through and a human may need to approve an exception. Signals carry messages into a running workflow. Queries read intermediate state without disturbing execution. A durable timer can sleep until next Tuesday and survive every deploy in between. Failure handling is built in rather than bolted on: automatic retries with backoff, four kinds of timeout, heartbeats, and saga compensation for the rollback path. Cloud pricing is stated plainly on the pricing page. You pay per action, starting at $50 per million and dropping to $25 at volume. Storage is separate, at $0.042 per GB-hour for active storage and $0.00105 for archival, and new accounts get $150 in credit.
What you pay in exchange is a learning curve. Workflow code has to be deterministic: the same input must produce the same decisions, because the server rebuilds state by replaying the event history. That rules out direct HTTP calls inside a workflow, random numbers, and reading the current time. All of it moves into Activities. People who have written one of these know the constraint bites during the first code review, usually on a line someone added casually. After launch comes version compatibility. Change the workflow logic and ask whether old histories can still replay. Temporal’s answer is Worker Versioning and replay tests. The tools exist. The habit has to be built, and it has to survive the week when the team is shipping something else.
There is an operational detail that rarely appears in the marketing. Every workflow carries an event history, and that history has a size. The longer a workflow runs and the more signals it receives, the larger the history grows and the slower replay becomes. Temporal’s answer is Continue-As-New, which starts a new execution at a chosen threshold and carries a summary of state across. The mechanism is not hard to use, but it changes your intuition about what “one workflow” means. Split points become a design decision rather than an afterthought.
Temporal therefore suits a team whose engineering practice is already established and who will pay upfront for long-term correctness. Its abstractions sit closer to distributed systems primitives than to a business process tool. Use it as a canvas and everything feels wrong. Use it as a runtime and the pieces you expect are all present, including the ones you only appreciate after the first incident.
Wrapping the functions you already have
Inngest starts from a different crowd. These are full-stack engineers writing Next.js, deployed on Vercel, with no queue, no SRE and no appetite for learning a new programming model to run one background job. Their problem is not orchestration theory. They have background work to do, no queue to put it in, and no wish to adopt a second programming model for it.
The approach is to wrap functions that already exist. You define a function with inngest.createFunction, and you wrap each unit of work in step.run. Each step retries on its own and stores its state automatically. Functions are triggered by events, cron schedules or webhooks, and Inngest calls back into your application over HTTPS, so nothing has to stay resident as a worker. Concurrency, throttling, debounce, rate limits and priority are configuration on the function, keyed on values such as event.data.userId, which keeps one noisy customer from consuming the capacity of everyone else. Local development runs on npx inngest-cli@latest dev, with a full dashboard on port 8288 showing every step of every run.
Onboarding cost is the lowest of the four. A team of three can connect the SDK in the afternoon and watch its first durable function complete before the evening. The SDKs cover TypeScript and JavaScript, Python, Go and others. Pricing is friendly to small teams: 50,000 executions a month on the free tier, a million at $99, ten million at $499, with maximum run duration rising across those tiers from 30 days to 90 to 366.
Two primitives fit support work especially well. waitForEvent parks a function until an external event arrives, such as a customer reply, then continues; if nothing arrives before the timeout, execution takes a different path. cancelOn lets a matching event cancel a flow that is already running, which handles the customer who cancels the order five minutes after opening the ticket. Combine those with steps and a good deal of what used to be a hand-written state machine becomes a few lines of configuration.
Two things get overlooked. The first is licensing. The Inngest server is under SSPL, with a future Apache 2.0 license attached. Self-hosting is technically possible and documented, but SSPL and Apache 2.0 are not the same license, and a company with a careful legal team should read the terms before building a dependency on it. The second is step boundaries. Recovery points are measured in steps, so a step that fails halfway retries as a whole. Pack a large block of logic into a single step and you have thrown away most of the recovery points you thought you had.
One binary, with state and calls inside the runtime
Restate is the youngest of the four and the most systematic about its own design. It describes itself as a low-latency runtime: a self-contained Rust binary that needs no external database and no message queue. Flows are still code, written in TypeScript, Python, Java, Kotlin, Go, Rust or Ruby, but calls pass through the runtime, which journals them. After a crash, execution resumes from the last completed call.
The architecture is a replicated command log plus materialized state, plus a push-based scheduler. The documentation frames this as moving coordination off the distributed read path. In plain terms, the call path carries one less cross-service lookup, so latency stays steadier.
What Restate adds beyond the other three is state. Virtual Objects shard by identity, and each object has its own key/value state with single-writer semantics. That turns “one durable session per user” into a primitive rather than something you assemble from a database table, a lock and a cache. Teams building coding agent platforms tend to like this shape: one session per object, with model calls, tool calls and human approvals all attached to the same object, so state and progress persist together and the latency stays inside the request path. The project’s homepage mentions Replit using it as the durability layer under its coding agent, and that class of workload is hard on both latency and state at the same time.
Deployment comes in three forms: open source self-hosted, Restate Cloud as a managed service, and BYOC, which runs the managed deployment inside your own AWS or GCP account. The license is BSL 1.1, converting to Apache 2.0 after four years. One clause in the additional use grant deserves a highlight. You may not operate it as a public Restate platform service, meaning third-party developers cannot register their own services and invoke them through your hosted platform. Internal use is fine. Running business flows for your own customers is fine. If the product you intend to sell is a workflow PaaS with outside developers as tenants, that clause blocks the plan.
Four products in one table
Putting four products into one table usually produces a pile of parameters, because their abstraction levels do not line up. Conductor manages a graph. Temporal manages the replay of code. Inngest manages boundaries between functions. Restate manages calls and state. So the table below compares only the four things that drive an actual decision: license, deployment, programming model and best fit. Pricing and ecosystem belong in prose, where the conditions attached to them survive the reading.
| Dimension | Conductor | Temporal | Inngest | Restate |
|---|---|---|---|---|
| License | Apache 2.0 | MIT for the server | SSPL, with a future Apache 2.0 license | BSL 1.1, Apache 2.0 after four years |
| Deployment | Self-hosted; Orkes sells a commercial cloud | Self-hosted plus Temporal Cloud | Self-hosted plus Inngest Cloud | Self-hosted, Cloud and BYOC |
| Programming model | JSON workflow definition, workers in many languages | Code as workflow, recovered by deterministic replay | Code plus step wrappers, HTTPS callbacks | Code plus runtime interception, durable handlers |
| Best fit | Flows that get reviewed and edited as diagrams | Long-running work, heavy compensation, strong engineering team | Small full-stack teams on serverless | Low latency, per-identity state, platform products |
A table like this can eliminate the obvious mismatches. It cannot pick for you, and if you finish reading it without a decision, that is a normal outcome rather than a failure of the table.
Choose by situation, not by feature checklist
If the flow itself is a business asset, one that legal, risk and operations all need to read, and one that changes often, Conductor is the least awkward fit. A JSON graph can be reviewed by people who do not write code. It can be versioned like any other document. It can be generated at runtime by a language model, which matters more each year. Put those properties together and you get a decent match for organizations where the flow changes frequently and the people changing it are not engineers. The price is accepting JSON as an expression language and accepting the operations work of self-hosting. The repository at https://github.com/conductor-oss/conductor lists the Apache 2.0 license and the five persistence backends in its README, and that README is the right first stop before anyone installs anything.
If a flow runs for a long time, carries compensation, receives signals and needs human approval, and the team already has Go or Java depth, Temporal is the safer choice. Its capability boundary is the clearest of the four, and its debugging path is the most developed when something goes wrong in production. What you accept in return is the determinism constraint, the discipline of replay testing, and the unpredictability of per-action billing. An agent loop where every iteration counts as an action will produce a bill that arrives faster than the architecture diagram suggested.
If the team is small, the product runs on serverless, and the goal is to ship this week rather than to survive an architecture review this quarter, Inngest offers the best return for the effort. The change to existing code is minimal and the mental load is light. Watch the SSPL terms, watch the step boundaries, and watch the 30-day run limit on the free tier, because a long-running job will eventually need a paid plan.
If the product is itself an agent platform, where every user and every session needs independent state and latency is part of the feature, Restate’s model fits most closely. A single binary removes the database and the queue from your operational scope, and Virtual Objects remove the work of stitching session state together by hand. Clear the BSL additional grant first, particularly if you plan to offer hosted capacity to anyone outside your own company.
One cost gets underestimated in every comparison of this kind. Self-hosting is not free. Conductor needs a JVM, a database and a message queue. Temporal needs a set of stateful services and persistent storage. Inngest’s self-hosted path needs its server and its queue maintained. Restate is the lightest of the four, and it still needs someone watching disks and snapshots. For a team of fewer than three people, converting that work into hours usually costs more than a year of managed service. For a team whose data cannot leave its own network, the same bill looks like a bargain.
There is also a case where none of the four is right. You have a handful of scheduled jobs, and when one fails you rerun it by hand and nobody notices. Introducing a durable execution engine there adds operational surface for a failure that costs nothing. A tool’s value tracks the cost of the failure it prevents. When failure is cheap, the tool is a burden with a dashboard.
Back to step seven
The support agent from the opening needs two changes. Move the refund call into a step that carries an idempotency key, and connect the whole chain to an engine that remembers where it stopped. The next network blip produces one refund and no conversation with finance.
At selection time, feature lists mislead, because all four engines persist state, retry failed work, ship a UI and support several languages. The differences that matter fit into three questions. After a crash, does your system start over or continue past step seven? Who owns the flow, the engineers or the business side? How much hidden cost in licensing and operations are you willing to carry for the next three years?
Answer those three honestly and the shortlist usually collapses to one name.
Related reading
- AI Agents Are Replacing Apps: From Chatbots to Autonomous Execution in 2026
- The Next AI Platform Divide: It’s Not About Bigger Models, It’s About Who Can Raise a Team of Agents
- Cursor 3 and the Shift from Writing Code to Managing Agents
- Power Automate AI vs AWS Bedrock Agents vs Google Vertex AI Agent Builder: Which Enterprise AI Automation Platform in 2026?



