Trigger.dev launched in February 2023 as a developer-first Zapier alternative. By October 2023, they pivoted to become a Temporal alternative for TypeScript. That shift from event-driven automation to durable execution orchestration exposes the infrastructure differences that matter when you move from stateless triggers to stateful agent workflows.
The distinction is not cosmetic. Event-driven systems fire webhooks and forget. Durable execution systems persist state, survive crashes, and guarantee exactly-once semantics across retries and long delays. Understanding the plumbing differences helps you choose the right tool when building multi-step AI agents, background jobs, or workflows that span hours or days.
What Durable Execution Guarantees That Webhooks Cannot
Event-driven automation relies on HTTP callbacks. A webhook fires, your handler runs, and the system moves on. If your handler crashes mid-execution, you lose context. If it takes 30 seconds and times out, you have no built-in retry with state preservation. If you need to wait for a human approval step, you must persist state yourself in a database and poll.
Durable execution systems like Temporal and Trigger.dev v2 provide:
- State persistence across failures: The orchestrator serializes execution state after each step. If a worker crashes, another worker resumes from the last checkpoint.
- Automatic retries with exponential backoff: Failed steps retry without losing prior context. You configure retry policies declaratively.
- Long-running workflows without timeouts: A workflow can sleep for days or weeks. The orchestrator persists the sleep state and wakes the workflow when the timer fires.
- Exactly-once execution semantics: The system deduplicates retries and ensures side effects happen once, even if the network duplicates requests.
Trigger.dev v2 implements these guarantees by wrapping TypeScript functions in a task abstraction. Each task runs in a managed worker pool. The platform serializes task state to durable storage after each step. If a worker dies, the scheduler replays the task from the last checkpoint on a different worker.
Queue Semantics and Worker Topology
Zapier-style systems use a simple queue model: an event arrives, a worker picks it up, executes a handler, and marks the job complete. If the handler fails, the job retries from scratch. There is no concept of partial progress.
Temporal-style systems use a workflow engine with event sourcing. Each workflow is a state machine. The engine logs every state transition to durable storage. Workers replay the event log to reconstruct state. This allows:
- Partial progress checkpoints: A workflow with 10 steps can fail at step 7 and resume at step 7, not step 1.
- Deterministic replay: The engine replays the event log to rebuild state. Side effects must be idempotent or wrapped in activities that the engine tracks separately.
- Concurrency control: You can configure how many instances of a task run concurrently. Trigger.dev exposes concurrency limits per task, not per queue.
Trigger.dev v2 uses a distributed scheduler that assigns tasks to workers. Workers pull tasks from the scheduler, execute them, and report progress. The scheduler persists task state in PostgreSQL. If a worker crashes, the scheduler reassigns the task to another worker and replays from the last checkpoint.
State Persistence and Retry Boundaries
The hardest part of durable execution is deciding what to persist and when. Trigger.dev v2 serializes task state after each await boundary. If your task calls an external API, the platform logs the request and response. If the task crashes before completing, the next worker replays the log and skips the API call, returning the cached response instead.
This works because TypeScript async functions are deterministic. The same inputs produce the same outputs. The platform intercepts async calls and logs them to the event log. Replay uses the log to reconstruct state without re-executing side effects.
Retry boundaries are explicit. You wrap fallible operations in try-catch blocks or configure retry policies at the task level. Trigger.dev retries failed tasks with exponential backoff. You can configure max attempts, backoff multipliers, and timeout thresholds.
export const processOrder = task({
id: "process-order",
retry: {
maxAttempts: 5,
factor: 2,
minTimeout: 1000,
maxTimeout: 60000,
},
run: async ({ orderId }: { orderId: string }) => {
// Fetch order from database (cached on replay)
const order = await db.orders.findUnique({ where: { id: orderId } });
// Call payment API (logged and cached on replay)
const payment = await stripe.charges.create({
amount: order.total,
currency: "usd",
source: order.paymentToken,
});
// Update order status (idempotent)
await db.orders.update({
where: { id: orderId },
data: { status: "paid", paymentId: payment.id },
});
// Send confirmation email (tracked as separate activity)
await sendEmail({
to: order.customerEmail,
subject: "Order confirmed",
body: `Your order ${orderId} is confirmed.`,
});
return { orderId, paymentId: payment.id };
},
});
If the task crashes after the Stripe call but before the database update, the next worker replays the event log, skips the Stripe call (using the cached response), and proceeds to the database update.
TypeScript-Native Orchestration vs. YAML Configs
Temporal uses a custom DSL for workflows. You write workflows in Go, Java, or TypeScript, but the engine serializes them to an internal format. Trigger.dev v2 uses plain TypeScript. Tasks are async functions. You compose tasks by calling them like normal functions.
This changes the developer experience:
- No DSL to learn: You use standard TypeScript syntax, async/await, and npm packages.
- Type safety end-to-end: Task inputs and outputs are typed. The compiler catches mismatches.
- Local testing: You can run tasks locally without deploying to the platform. The SDK provides a local runner that simulates the scheduler.
- Familiar debugging: You use standard Node.js debugging tools. The platform streams logs in real time.
The trade-off is less flexibility. Temporal workflows can span multiple languages and services. Trigger.dev v2 tasks are TypeScript-only. If you need to call Python code, you must wrap it in a subprocess or HTTP API.
Failure Modes and Observability
Durable execution systems expose new failure modes:
- Non-deterministic replay: If your task uses
Math.random()orDate.now(), replay produces different results. The platform cannot reconstruct state. Trigger.dev detects non-determinism and fails the task. - State bloat: If your task accumulates large objects in memory, the serialized state grows. The platform limits state size to prevent database bloat.
- Replay storms: If a task fails repeatedly, the scheduler retries it on every worker. This can overwhelm downstream services. Trigger.dev rate-limits retries per task.
Observability is critical. Trigger.dev v2 provides:
- Real-time logs: The platform streams logs from workers to the dashboard. You see logs as the task executes.
- Execution traces: The platform visualizes the task execution graph. You see which steps succeeded, which failed, and how long each took.
- Retry history: The dashboard shows every retry attempt, including error messages and stack traces.
- Concurrency metrics: You see how many instances of each task are running, queued, or failed.
Architecture Comparison
| Dimension | Event-Driven (Zapier-style) | Durable Execution (Temporal-style) |
|---|---|---|
| State persistence | Manual (database or cache) | Automatic (event log) |
| Retry semantics | Restart from scratch | Resume from checkpoint |
| Long delays | Poll or cron | Sleep with timer |
| Concurrency control | Queue-level | Task-level |
| Failure recovery | Re-execute handler | Replay event log |
| Developer experience | Webhook handlers | Workflow functions |
| Observability | Logs and metrics | Execution traces |
When to Use Trigger.dev v2
Use Trigger.dev v2 when:
- You need multi-step workflows that survive crashes and retries.
- Your tasks run longer than typical serverless timeouts (15 minutes).
- You need exactly-once execution semantics without manual deduplication.
- You want to compose tasks in TypeScript without learning a workflow DSL.
- You need concurrency control and rate limiting per task.
Avoid Trigger.dev v2 when:
- Your tasks are stateless and finish in seconds. Use a simple queue like BullMQ or AWS SQS.
- You need polyglot workflows that span multiple languages. Use Temporal.
- You need sub-second latency. Durable execution adds overhead for state persistence.
- Your tasks have large state objects. The platform limits serialized state size.
Technical Verdict
Trigger.dev v2 exposes the plumbing differences between event-driven automation and durable execution orchestration. The shift from webhooks to workflows requires state persistence, deterministic replay, and retry boundaries. The TypeScript-native API lowers the learning curve compared to Temporal, but limits you to a single language.
For AI agent orchestration, the trade-offs favor durable execution. Agents make multiple API calls, wait for human input, and retry failed steps. Trigger.dev v2 handles these patterns without manual state management. The observability tools help debug non-deterministic failures and retry storms.
The platform is open source, so you can self-host or inspect the scheduler implementation. The managed service handles worker scaling and state persistence. If you are building multi-step workflows in TypeScript and need more than a simple queue, Trigger.dev v2 is worth evaluating.