yaml
title: “Trigger.dev V2: Durable Execution Plumbing for TypeScript Without Temporal’s Complexity” description: “How Trigger.dev pivoted from event triggers to durable execution, exposing state persistence, retry primitives, and serverless timeout workarounds.” pubDate: 2026-06-08T04:11:18.402Z category: dev-tools heroImage: “https://images.unsplash.com/photo-1568716353609-12ddc5c67f04?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w4Njk2ODR8MHwxfHNlYXJjaHwxfHxUcmlnZ2VyLmRldiUyMFYyJTNBJTIwV2hhdCUyMGElMjBUZW1wb3JhbCUyMEFsdGVybmF0aXZlJTIwZm9yJTIwVHlwZVNjcmlwdCUyMFJldmVhbHMlMjBBYm91dCUyMER1cmFibGUlMjBFeGVjdXRpb24lMjBQbHVtYmluZyUyMHNvZnR3YXJlJTIwaW5mcmFzdHJ1Y3R0dXJlJTIwZGFzaGJvYXJkfGVufDF8MHx8fDE3ODA3OTA1MTl8MA&ixlib=rb-4.1.0&q=80&w=1080” sourceUrl: “https://trigger.dev” sourceName: “trigger.dev” tags: [“agentic-ai”, “orchestration”, “infrastructure”] featured: false
Trigger.dev launched in February 2023 as a “developer-first Zapier alternative” (745 HN points). By October, the team shipped V2 as a “Temporal alternative for TypeScript devs” (172 points). That pivot reveals what developers building agent workflows and background automation actually need: not event routing, but durable execution guarantees.
The shift exposes the plumbing differences between workflow orchestration systems and long-running task reliability. Temporal solves this with workflows, activities, and explicit state machines. Trigger.dev bets TypeScript developers want the guarantees without the ceremony.
What Changed Between V1 and V2
V1 architecture:
- Event-driven triggers (webhooks, schedules, API calls)
- Zapier-style connectors and integrations
- Stateless function execution model
V2 architecture:
- Durable task execution with automatic retries
- State persistence across infrastructure failures
- Serverless timeout workarounds for long-running jobs
- TypeScript-first SDK without separate workflow DSL
The feedback loop was clear: developers wanted reliable background jobs more than they wanted integration glue. AI agent workflows, media processing pipelines, and human-in-the-loop approval flows all need the same thing: tasks that survive crashes and resume where they left off.
Durable Execution Guarantees Without Manual Idempotency
Trigger.dev’s core promise is that you write normal TypeScript functions and get durability for free. The platform handles:
Automatic checkpointing:
- Every
awaitboundary becomes a checkpoint - State serialized to Postgres after each step
- Function resumes from last checkpoint on failure
Retry primitives:
- Exponential backoff with configurable limits
- Per-task retry policies
- Dead letter queue for permanent failures
Execution guarantees:
- At-least-once delivery by default
- Idempotency keys for exactly-once semantics
- Timeout handling without serverless limits
Here’s what a durable task looks like:
import { task } from "@trigger.dev/sdk/v3";
export const processVideo = task({
id: "process-video",
retry: {
maxAttempts: 3,
factor: 2,
minTimeout: 1000,
maxTimeout: 10000,
},
run: async (payload: { videoUrl: string }) => {
// Each await is a checkpoint
const download = await downloadVideo(payload.videoUrl);
// If this fails, resume from here
const transcoded = await transcodeVideo(download.path);
// State persisted automatically
const uploaded = await uploadToS3(transcoded.path);
return { url: uploaded.url };
},
});
The SDK intercepts every async call, serializes state, and stores it. On retry, the function skips completed steps and resumes from the failure point. You don’t write idempotent handlers manually.
State Persistence Strategy
Trigger.dev uses Postgres as the state store, not an event log like Temporal’s Cassandra/MySQL backend. The trade-off:
Postgres approach:
- Simpler operational model (one database)
- Easier to query task state directly
- Limited horizontal scaling compared to distributed logs
- Checkpoint size constrained by row limits
Storage schema:
- Task runs table with status, attempts, checkpoints
- Checkpoint blobs stored as JSONB
- Execution logs linked by run ID
- Scheduled tasks in separate cron table
For most TypeScript workloads (API orchestration, agent tool calls, batch jobs), Postgres is fast enough. If you’re running millions of concurrent tasks, you’ll hit scaling limits before Temporal would.
Temporal Comparison: Workflow Model vs. Function Model
| Aspect | Temporal | Trigger.dev |
|---|---|---|
| Programming model | Workflows + Activities (separate concerns) | Single task function with checkpoints |
| State machine | Explicit workflow DSL | Implicit from async/await |
| Language support | Go, Java, TypeScript, Python, PHP | TypeScript only |
| Execution guarantees | Exactly-once via deterministic replay | At-least-once with idempotency keys |
| Timeout handling | Infinite workflow duration, activity timeouts | Serverless escape hatch (no hard limits) |
| Operational complexity | Self-hosted cluster (Cassandra, workers, frontend) | Managed platform or Docker Compose |
| Observability | Temporal UI, event history | Trigger.dev dashboard, OpenTelemetry |
Temporal’s workflow/activity split forces you to think about determinism and side effects. Trigger.dev hides that: every function is a workflow, every await is an activity boundary. You lose fine-grained control but gain simplicity.
Retry and Backoff Primitives
Trigger.dev exposes retry configuration at the task level:
export const apiCall = task({
id: "api-call",
retry: {
maxAttempts: 5,
factor: 2, // Exponential backoff multiplier
minTimeout: 500, // First retry after 500ms
maxTimeout: 30000, // Cap at 30s
randomize: true, // Add jitter
},
run: async (payload) => {
// Automatic retry on throw
const response = await fetch(payload.url);
if (!response.ok) throw new Error("API failed");
return response.json();
},
});
Comparison to Temporal’s saga patterns:
- Temporal: You write compensation logic manually (undo steps on failure)
- Trigger.dev: No built-in saga support; you handle rollback in application code
- Temporal: Retry policies per activity, workflow-level error handling
- Trigger.dev: Retry policies per task, no nested workflow concept
For agent workflows that call LLM APIs, the Trigger.dev model is simpler. For complex multi-step transactions (e.g., booking a flight + hotel + car), Temporal’s saga primitives are more powerful.
Serverless Timeout Workarounds
Serverless platforms (Vercel, AWS Lambda) impose 15-minute to 30-minute timeouts. Trigger.dev’s architecture sidesteps this:
Execution model:
- Tasks run on Trigger.dev’s managed workers (not your serverless function)
- Your app triggers tasks via SDK, workers execute them
- Workers have no timeout limits
- State checkpointed to Postgres every few seconds
Self-hosted option:
- Docker Compose with Postgres + worker containers
- Workers poll task queue, execute functions
- Same checkpoint/retry logic as managed platform
This means you can run a 6-hour video transcoding job or a multi-day agent research task without hitting Lambda limits. The trade-off: you’re deploying code to Trigger.dev’s infrastructure, not your own serverless runtime.
Concurrency and Queue Control
Trigger.dev exposes queue primitives that Temporal handles at the worker pool level:
export const batchJob = task({
id: "batch-job",
queue: {
name: "batch-queue",
concurrencyLimit: 10, // Max 10 concurrent runs
},
run: async (payload) => {
// Process batch item
},
});
Queue features:
- Named queues with independent concurrency limits
- Priority levels (high, normal, low)
- Rate limiting (max tasks per second)
- FIFO ordering within priority bands
For AI agent workflows that call rate-limited APIs (OpenAI, Anthropic), you set concurrency limits to avoid 429 errors. Temporal requires you to configure worker pools and task queues separately.
Observability and Failure Modes
Trigger.dev’s dashboard shows:
- Task run history with status (pending, running, success, failed)
- Execution logs with timestamps
- Checkpoint replay timeline
- Retry attempts and backoff intervals
Likely failure modes:
-
Checkpoint serialization errors: If your task state includes non-serializable objects (class instances, functions), checkpoints fail. Solution: use plain objects and primitives.
-
Postgres connection exhaustion: High-concurrency workloads can saturate connection pools. Solution: tune pool size or shard by queue.
-
Non-deterministic code: If your task reads
Date.now()orMath.random()across checkpoints, replays produce different results. Solution: pass timestamps/seeds as payload. -
Memory leaks in long-running tasks: Workers don’t restart between checkpoints. Solution: monitor heap usage, restart workers periodically.
-
Idempotency key collisions: If you trigger the same task twice with the same key, the second call is ignored. Solution: use UUIDs or timestamps in keys.
Deployment Shape
Managed platform:
- Sign up, deploy tasks via CLI
- Workers auto-scale based on queue depth
- Postgres managed by Trigger.dev
- Pay per task execution
Self-hosted:
- Docker Compose with
trigger-worker,postgres,redis - Deploy task code as npm package
- Workers pull from queue, execute tasks
- You manage scaling and backups
The managed platform is simpler but locks you into Trigger.dev’s infrastructure. Self-hosting gives you control but requires operational overhead (database backups, worker scaling, monitoring).
Technical Verdict
Use Trigger.dev when:
- You’re building TypeScript-first agent workflows or background jobs
- You need durable execution without learning Temporal’s workflow DSL
- Your tasks are I/O-bound (API calls, database queries, file processing)
- You want managed infrastructure with minimal ops overhead
- Your concurrency needs fit within Postgres scaling limits (thousands of tasks/sec)
Avoid Trigger.dev when:
- You need exactly-once semantics with complex saga patterns
- Your workload requires millions of concurrent tasks
- You’re already invested in Temporal’s ecosystem (Go/Java workers, existing workflows)
- You need fine-grained control over workflow determinism and replay
- You require multi-language support (Python workers, Java activities)
Trigger.dev’s bet is that most TypeScript developers building agent orchestration, media pipelines, or batch jobs don’t need Temporal’s full power. They need reliable retries, state persistence, and a simple programming model. The V1-to-V2 pivot proves that bet resonated: developers wanted durability, not integration glue.
Source Links
- Primary source: Trigger.dev
- V2 Show HN discussion: Hacker News (172 points, 39 comments)
- V1 Show HN discussion: Hacker News (745 points, 190 comments)
- GitHub repository: triggerdotdev/trigger.dev