---
title: Workflow SDK vs trigger.dev
description: How the Workflow SDK compares to trigger.dev, including deterministic event-log replay versus CRIU process checkpoint/restore and a concept-mapping migration guide.
type: conceptual
summary: trigger.dev achieves durability by snapshotting the process with Checkpoint/Restore in Userspace (CRIU), so code has no determinism constraints. The Workflow SDK uses event-log replay and runs in your existing app.
prerequisites:
  - /docs/foundations/workflows-and-steps
related:
  - /docs/foundations/errors-and-retries
  - /docs/foundations/streaming
  - /docs/ai
---

[trigger.dev](https://trigger.dev) is an open-source, TypeScript-first durable task platform. Instead of replaying code, it **snapshots the whole process** with Checkpoint/Restore in Userspace (CRIU) at each wait point and restores it later. This design drives most of the differences with the Workflow SDK.

<Callout type="info">
**Choose the Workflow SDK** when you want durable orchestration that runs in your existing app, a portable open-source backend you can self-host, TypeScript and Python, and broad framework support. **Choose trigger.dev** when you want a managed task platform with no determinism constraints and use only TypeScript.
</Callout>

## At a glance

| | Workflow SDK | trigger.dev |
| --- | --- | --- |
| **Category** | Open-source durable-functions SDK that runs in your app | Durable task platform with its own runtime (Cloud or self-hosted) |
| **Durability model** | Event log + **deterministic replay** (workflow body must be deterministic) | **Process checkpoint/restore (CRIU)**: snapshots memory, CPU, and file descriptors; **no determinism constraints**, code runs as-is |
| **Authoring** | `"use workflow"` / `"use step"` in your existing app | `task()` / `schemaTask()` deployed to trigger.dev as a separate target (Docker image) |
| **Languages** | TypeScript / JavaScript (Python beta) | **TypeScript / JavaScript only** |
| **Where it runs** | Co-located with your app (Vercel managed or self-host) | trigger.dev's run engine (isolated containers) |
| **Versioning** | Runs pinned to immutable deployment, safe by default | **Atomic versioning**: runs lock to their deploy version; new deploys never touch in-flight runs (same safety property) |
| **AI and streaming** | `WorkflowAgent` in the AI SDK; native resumable streaming | AI SDK tools, native `useChat` transport, resumable Realtime, durable multi-turn Sessions, human-in-the-loop (HITL) via `wait.forToken` |
| **Concurrency control** | Enforce in steps / at the publisher | First-class queues + concurrency keys |
| **Portability** | Apache-2.0; World abstraction; runs anywhere Node runs | Apache-2.0; self-host on Docker/Kubernetes, but CRIU needs a compatible host (heavier than plain Docker); TypeScript-only |
| **Pricing** | SDK free; pay your platform | Cloud: compute-seconds + per-run ($0.0000338/s Small + $0.000025/run); no charge while checkpointed |
| **Limits** | 50 MB payload; 2 GB/run; no duration cap ([Vercel World limits](https://vercel.com/docs/workflows/pricing)) | 3 MB payload / 10 MB output; 14-day maximum run lifetime; CPU-time-based maximum duration |

**What the limits mean in practice**: trigger.dev caps task payloads at 3 MB and outputs at 10 MB (large model contexts and transcripts need external storage), and the 14-day maximum run time means human-in-the-loop flows that wait longer than two weeks can't complete in one run. The [Vercel World limits](https://vercel.com/docs/workflows/pricing) are 50 MB payloads, 2 GB of state per run, and no run-duration cap.

## The core difference: checkpoint/restore vs. replay

trigger.dev freezes the entire operating system process with CRIU when a task hits a wait point, then restores it later, so there's **no replay and no determinism rule**: you can call `Date.now()` or `Math.random()` anywhere, and prior steps don't re-execute. The cost is an execution model that requires CRIU-capable infrastructure (which makes self-hosting heavier than a plain container) and runs on trigger.dev's runtime as a separate deploy target.

The Workflow SDK reconstructs state by **replaying the workflow function** against its event log. That requires the workflow body to be deterministic (side effects go in `"use step"` functions), but it runs inside your existing app and deployment with no special host requirements, and the [World abstraction](/worlds/building-a-world) lets you swap the storage/queue/stream layers.

Notably, **both pin runs to a version** so deploys never corrupt in-flight work: trigger.dev via atomic version-locking, the Workflow SDK via immutable-deployment pinning.

## AI agents

trigger.dev offers AI SDK tool wrapping, a native `useChat` transport over its Realtime layer, resumable streaming, and durable multi-turn Sessions. The Workflow SDK offers `WorkflowAgent` directly inside the AI SDK plus native [resumable streaming](/docs/ai/resumable-streams). Both support human-in-the-loop workflows (trigger.dev's `wait.forToken` and the Workflow SDK's hooks). Key differences are language support (trigger.dev is TypeScript-only; the Workflow SDK adds Python) and whether the agent runs in your app or on a dedicated platform.

## Migrating from trigger.dev

| trigger.dev | Workflow SDK | Note |
| --- | --- | --- |
| `task({ id, run })` | `"use workflow"` function started with `start()` | No factory or id registry. |
| `schemaTask({ schema, run })` | Typed function + `"use workflow"` | Validate inputs at the call site. |
| Inline `run` body | `"use step"` functions | Side effects move into named steps. |
| `wait.for` / `wait.until` | `sleep('5m')` / `sleep(date)` | Import from `workflow`. |
| `wait.forToken({ timeout })` | `createHook()` + `Promise.race` with `sleep()` | Hooks carry a typed token. |
| `triggerAndWait()` | [`start()`](/docs/foundations/starting-workflows) called directly from the workflow, then await the returned `Run` | Spawn + collect. |
| `batch.triggerAndWait()` | `Promise.all` over collected `Run` handles | Standard concurrency. |
| `metadata.stream()` / Realtime | `getWritable()` / named streams | Clients read from the stream. |
| Run tags / `metadata.set()` | [`setAttributes()`](/docs/api-reference/workflow/set-attributes) / `attributes` option on `start()` | Filter runs by `key=value`. See [Attributes](/docs/observability/attributes). |
| `AbortTaskRunError` | `FatalError` | Stops retries immediately. |

The `task()` factory collapses into a plain function, and because the workflow body is replayed, move side effects into steps:

```typescript title="workflows/order.ts"
export async function processOrder(orderId: string) {
  'use workflow'; // [!code highlight]
  const order = await loadOrder(orderId);
  return { orderId: order.id, status: 'completed' };
}

async function loadOrder(orderId: string) {
  'use step'; // [!code highlight]
  const res = await fetch(`https://example.com/api/orders/${orderId}`);
  return res.json() as Promise<{ id: string }>;
}
```

<Callout type="warn">
trigger.dev's `run` body has full Node.js access. The Workflow SDK's `"use workflow"` body runs in a sandboxed virtual machine (VM). Side effects (`fetch`, `Date.now()`, `Math.random()`, and database access) must live inside `"use step"` functions. Orchestration stays in the workflow body.
</Callout>

<Callout type="info">
Install the migration skill to translate a trigger.dev app automatically:

```bash
npx skills add https://github.com/vercel/workflow --skill migrating-to-workflow-sdk
```
</Callout>

### trigger.dev features without a direct Workflow SDK equivalent

Each row is a trigger.dev capability the Workflow SDK does not replicate one-to-one, paired with how to cover it on the Workflow SDK side:

| trigger.dev feature | How to cover it with the Workflow SDK |
| --- | --- |
| Concurrency keys / queue concurrency limits | Enforce limits inside steps or debounce at the publisher |
| `schedules.task()` / cron | Trigger from Vercel Cron or a system cron calling `start()` |
| `machine` presets / custom images | Function resources are per-deployment (configured via your hosting platform) |

---
*Compiled from public documentation. Verify current trigger.dev limits and pricing against [trigger.dev/docs](https://trigger.dev/docs). Not based on head-to-head benchmarks.*
