Skip to main content

Guide

Inngest vs Trigger.dev vs Restate: Durable Workflows (2026)

Compare Inngest, Trigger.dev, and Restate for durable TypeScript work: retries, waits, execution model, deployment ownership, and service boundaries.

·PkgPulse Team·
0
Hero image for Inngest vs Trigger.dev vs Restate: Durable Workflows (2026)

TL;DR

Choose Inngest when you want event-, cron-, or webhook-triggered durable functions to stay close to an existing application. Choose Trigger.dev when you want separately deployed long-running tasks with explicit machines, queues, waits, retries, and checkpoint/resume behavior. Choose Restate when durable execution, keyed state, and service/workflow boundaries should become part of the system architecture. Compare failure semantics and deployment ownership before comparing syntax.

Quick Comparison

PlatformUnit of workTrigger modelWhere code runsStrongest fitMain commitment
InngestDurable function made of retriable stepsEvents, cron schedules, and webhooksFunction code runs on your compute and is coordinated by InngestAdding durable background logic to an existing web/serverless applicationAdopt Inngest's event, function, and step model plus its coordination plane
Trigger.devDeployed task/run with queues, waits, retries, and checkpointsSDK/API triggers and schedulesTask code is built and deployed to Trigger.dev cloud or a self-hosted instanceLong-running Node.js/TypeScript jobs needing dedicated execution resourcesOperate a separate task deployment and its worker/runtime lifecycle
RestateDurable service handler, workflow, or virtual objectCalls routed through the Restate runtimeYour handlers run as services while Restate journals invocations, state, timers, and effectsSystems where durable execution and keyed state cross service boundariesIntroduce a log-first runtime and model services around its semantics

Why this matters in 2026

Traditional queue advice is no longer enough. A lot of modern backend work is not just “run this later.” It is:

  • wait for an external event without losing state
  • retry only the failed step, not the whole job
  • coordinate AI calls, webhooks, approvals, and database updates
  • survive deploys and worker restarts without writing custom compensation code

That is the problem Inngest, Trigger.dev, and Restate are all trying to solve. They overlap, but they do not sit at the same layer.

What actually changes the decision

  • Failure boundary: decide whether retries replay a step, resume a checkpointed task, or replay a journaled service/workflow invocation.
  • Deployment boundary: identify who builds and runs the worker code, who stores durable state, and what must be restored after a control-plane or worker outage.
  • Idempotency: test duplicate events, duplicate triggers, partial side effects, and retry exhaustion with your real payment/email/database integrations.
  • Waiting: verify how long sleeps, human approvals, child jobs, and external callbacks release compute and resume after deploys.
  • Versioning: confirm what happens to in-flight work when code changes. Durable execution turns deployment compatibility into a data-lifecycle concern.
  • Ignore client bundle size. These are server-side systems; execution, storage, observability, and recovery matter more.

Package-by-package breakdown

Inngest

Package: inngest

Inngest wins on time-to-value. It feels like the least disruptive path from “I have a web app” to “I have durable workflows with retries and waits.”

import { Inngest } from "inngest";

export const inngest = new Inngest({ id: "pkgpulse-app" });

export const sendDigest = inngest.createFunction(
  { id: "send-digest" },
  { event: "digest/requested" },
  async ({ event, step }) => {
    const user = await step.run("load-user", () => db.user.findUnique({ where: { id: event.data.userId } }));
    await step.run("send-email", () => emailDigest(user));
  }
);

Why teams pick it:

  • It slots into existing serverless and Next.js apps with very little ceremony.
  • The step.run() model is easy for most application developers to understand.
  • Event-driven flows, scheduled work, and waits all feel like extensions of normal app code.

Watch-outs:

  • If you want deeper runtime control or a self-hosted-first posture, Trigger.dev is often a better fit.
  • If you need durability to be a cross-service architectural property, Restate is stronger.

Trigger.dev

Package: @trigger.dev/sdk

People still say “Trigger.dev v3” because the big rewrite is what changed the category for many Node.js teams. The important practical point in 2026 is that the current SDK continues that model: long-running tasks, waits, schedules, and a very good JavaScript-native authoring experience.

import { task, wait } from "@trigger.dev/sdk/v3";

export const generateReport = task({
  id: "generate-report",
  run: async (payload: { reportId: string }) => {
    const data = await fetchInputs(payload.reportId);
    await wait.for({ seconds: 30 });
    return finalizeReport(data);
  },
});

Why teams pick it:

  • The authoring model feels close to normal async JavaScript instead of workflow DSLs.
  • It covers common production needs well: schedules, waits, retries, and background execution.
  • Self-hosting is not an afterthought, which matters for teams that do not want all job infrastructure outsourced.

Watch-outs:

  • The version story can be confusing because the “v3” label stuck in the ecosystem conversation while the SDK kept evolving.
  • If you want the simplest possible adoption inside an existing Next.js codebase, Inngest usually gets there faster.

Restate

Package: @restatedev/restate-sdk

Restate is the most infrastructure-flavored option in this comparison. It is not just background jobs with retries. It is a durable execution layer for services and workflows.

import * as restate from "@restatedev/restate-sdk";

export const billingWorkflow = restate.workflow({
  name: "billingWorkflow",
  handlers: {
    run: async (ctx, input: { customerId: string }) => {
      const invoice = await ctx.serviceClient(invoiceService).create(input);
      await ctx.sleep("15m");
      return ctx.serviceClient(notificationService).send({ invoiceId: invoice.id });
    },
  },
});

Why teams pick it:

  • Durable state and replay are part of the programming model, not bolted on around individual task steps.
  • It is a better match for business-critical workflows that cross service boundaries.
  • Teams that care about correctness, idempotency, and recovery semantics tend to appreciate its model more over time.

Watch-outs:

  • It is the heaviest conceptual lift here.
  • If your actual need is “reliable background jobs for my app,” Restate can be more system than you need.

Failure and recovery model

Inngest documents functions as durable, retriable units of background logic. Steps provide the durable checkpoints: successful work is recorded so a later retry can continue without repeating every completed operation. This is a good fit when the application can expose function handlers and express work as events plus steps. You still need idempotency around external side effects and a policy for terminal failures.

Trigger.dev runs task code in a separate execution environment. Its documented checkpoint/resume system can suspend a task during waits or child-task coordination, release the execution resource, and restore state when work resumes. That model is attractive for long-running Node.js code and resource-specific jobs, but it means task deployment, machine sizing, queue/concurrency policy, and self-hosted-instance operations are part of the platform decision.

Restate is different in depth. The runtime sits between callers and handlers, records invocations and effects in a durable log, routes keyed workflows or virtual objects to partitions, and materializes state for execution. That can simplify correctness across services, but it is not a drop-in replacement for a queue call. Teams need to design service keys, idempotency, versioning, storage, and cluster recovery around the runtime.

Deployment decision checklist

Run a failure-oriented prototype before choosing:

  1. Start one workflow, kill the worker after the first external side effect, and verify exactly what reruns.
  2. Deploy changed code while a workflow is sleeping, then verify version and resume behavior.
  3. Send the same event or trigger twice and confirm deduplication plus application-level idempotency.
  4. Exhaust retries and confirm where the failure is visible, how an operator retries it, and whether compensation runs.
  5. Test a dependency outage longer than the retry window and a control-plane outage separate from a worker outage.
  6. Measure storage/retention needs for histories, logs, payloads, checkpoints, and durable state.
  7. Confirm how secrets, private networking, regional placement, backups, and upgrades work in the deployment model you will actually use.

Which one should you choose?

  • Choose Inngest for durable functions that should remain close to an existing application and are naturally triggered by events, cron, or webhooks.
  • Choose Trigger.dev for separately deployed long-running tasks where Node.js-native code, execution resources, queues, waits, and self-hosting are central.
  • Choose Restate when durable service calls, keyed state, timers, and workflows are architectural primitives rather than a background-job convenience.

Do not choose on a “guaranteed completion” slogan alone. No platform can make a non-idempotent external side effect safe automatically. The winning prototype is the one whose replay, retry, visibility, and operator-recovery behavior your team can predict under failure.

Sources checked

Official documentation reviewed July 23, 2026:

  • Inngest Functions — events/cron/webhook triggers, durable retriable functions, step-level state, automatic retries, and execution on the application's compute.
  • Trigger.dev: How it works — deployed long-running tasks, cloud/self-hosted instances, retries, worker architecture, and checkpoint/resume behavior.
  • Restate architecture — ingress, durable log, partition processors, keyed routing, state materialization, snapshots, and failover model.

Pricing and package-version claims are intentionally omitted because they change faster than the execution models. Recheck current first-party limits for runs, compute, concurrency, retention, payloads, and self-hosting before estimating cost.

Hatchet vs Trigger.dev v3 vs Inngest · Temporal vs Restate vs Windmill · Best Node.js Background Job Libraries 2026

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.