Trigger.dev launched in February 2023 as a “developer-first Zapier alternative” and earned 745 Hacker News points. Eight months later, the team shipped V2 with a completely different pitch: a Temporal alternative for TypeScript developers. That pivot tells you everything about the gap between event-driven webhook chains and the durable execution primitives that multi-hour agent workflows actually need.
The architectural shift is not cosmetic. V1 handled webhook triggers and simple task chains. V2 exposes queue semantics, retry boundaries, state persistence, and long-running execution guarantees. If you are building agents that call tools, wait for human approval, or run for minutes instead of milliseconds, you need these primitives. Trigger.dev’s TypeScript-first surface makes them accessible without learning Temporal’s Go-centric mental model.
Why the Pivot Happened
Early users wanted more than webhook glue. They needed:
- Retry boundaries around individual steps, not just entire workflows
- State that survives process crashes during multi-hour runs
- Concurrency controls to prevent API rate limit violations
- Observability into task execution, not just success/failure logs
Zapier-style automation assumes tasks finish in seconds. Agent orchestration assumes tasks can stall, fail partway through, or need to resume after external input. The infrastructure requirements are fundamentally different.
Durable Execution Primitives Exposed
Trigger.dev’s task model gives you explicit control over execution guarantees:
export const researchAgent = task({
id: "research-agent",
retry: {
maxAttempts: 3,
factor: 2,
minTimeout: 1000,
maxTimeout: 10000,
},
queue: {
concurrencyLimit: 5,
},
run: async ({ topic }: { topic: string }) => {
const messages: CoreMessage[] = [
{ role: "user", content: `Research: ${topic}` },
];
for (let i = 0; i < 10; i++) {
const { text, toolCalls, steps } = await generateText({
model: anthropic("claude-opus-4-20250514"),
system: "You are a research assistant with web access.",
messages,
tools: { search, browse, analyze },
maxSteps: 5,
});
if (!toolCalls.length) {
return { summary: text, stepsUsed: steps.length };
}
for (const call of toolCalls) {
const result = await executeTool(call);
messages.push({ role: "tool", content: result });
}
}
},
});
The task wrapper handles:
- Automatic retries with exponential backoff when tool calls fail
- Queue-based concurrency so you do not hammer external APIs
- State checkpointing after each tool execution
- Execution replay from the last successful checkpoint on crash
This is not magic. It is infrastructure you would otherwise build yourself with Redis, Postgres, and a custom worker pool.
Retry Boundaries vs. Temporal’s Activity Model
Temporal splits workflows into activities. Each activity is a separate retry boundary with its own timeout and failure semantics. Trigger.dev uses a simpler model: the entire task is the retry boundary, but you can checkpoint state explicitly.
| Aspect | Temporal | Trigger.dev |
|---|---|---|
| Retry scope | Per-activity (fine-grained) | Per-task (coarse-grained) |
| State persistence | Automatic event sourcing | Explicit checkpoints |
| Timeout handling | Activity and workflow timeouts | Task-level timeout |
| Partial failure | Activity retries independently | Entire task retries from checkpoint |
| Developer surface | Workflow DSL + activity functions | Standard async TypeScript |
Temporal’s model gives you more control but requires understanding workflows, activities, signals, and queries. Trigger.dev’s model is simpler: write async functions, add retry config, let the platform handle execution.
For agent workflows, this trade-off matters. If your agent calls ten tools in sequence and tool seven fails, do you want to retry just that tool (Temporal) or replay from the last checkpoint (Trigger.dev)? The answer depends on whether your tools are idempotent and how expensive earlier steps were.
Queue Semantics and Concurrency Control
Trigger.dev exposes queues as first-class primitives. You can:
- Limit concurrent executions per queue to respect API rate limits
- Prioritize tasks within a queue
- Delay execution until a specific time
- Batch tasks to reduce overhead
This is critical for agent orchestration. If your agent calls an LLM API with a 10-request-per-minute limit, you need queue-level concurrency control. Without it, you either throttle in application code (fragile) or hit rate limits and waste retries (expensive).
export const batchProcessor = task({
id: "batch-processor",
queue: {
name: "llm-calls",
concurrencyLimit: 10,
},
run: async (batch: string[]) => {
// Only 10 of these run concurrently across all instances
return await Promise.all(
batch.map(item => callLLM(item))
);
},
});
Temporal requires you to implement concurrency limits using signals or external coordination. Trigger.dev bakes it into the queue abstraction.
State Persistence and Observability
Trigger.dev persists execution state to Postgres. Every task run gets:
- Execution logs with timestamps and step boundaries
- Retry history showing which attempts failed and why
- State snapshots at checkpoint boundaries
- Trace IDs for distributed tracing
The observability dashboard shows:
- Tasks currently running
- Queue depth and concurrency usage
- Retry attempts and failure rates
- Execution duration histograms
This is table stakes for production agent systems. You need to know when an agent is stuck, which tool call failed, and whether retries are making progress or burning money.
Deployment Shape and Hosting Model
Trigger.dev is open source but designed for managed hosting. The deployment model is:
- Developer writes tasks in TypeScript using the SDK
- CLI deploys tasks to Trigger.dev’s managed workers
- Platform handles execution, retries, and state persistence
- Developer triggers tasks via API or SDK
You can self-host, but the managed service handles:
- Worker scaling based on queue depth
- State database backups and replication
- Log aggregation and retention
- Metrics and alerting
For teams building agents, this is the right trade-off. You want to write orchestration logic, not operate a distributed task queue.
Failure Modes and Operational Risks
Trigger.dev’s architecture introduces specific failure modes:
State database becomes a bottleneck. Every task execution writes state to Postgres. High-throughput workflows can saturate the database. Mitigation: shard tasks across multiple queues or use batch checkpointing.
Retry storms amplify external failures. If an external API goes down, all tasks retry simultaneously. Mitigation: use exponential backoff and circuit breakers in tool implementations.
Long-running tasks hold worker slots. A task that runs for hours blocks a worker. Mitigation: split long tasks into smaller subtasks or use scheduled tasks for periodic work.
Managed service lock-in. The managed hosting model is convenient but creates dependency. Mitigation: test self-hosted deployment early if vendor lock-in is a concern.
When TypeScript Ergonomics Matter
Temporal’s Go and Java SDKs are mature but verbose. The TypeScript SDK exists but feels like a port. Trigger.dev is TypeScript-native, which means:
- No workflow/activity boilerplate. Just write async functions.
- Type-safe task definitions. Input and output types are inferred.
- Standard npm tooling. No custom build steps or code generation.
- Native async/await. No special workflow syntax to learn.
For teams already using TypeScript for backend services, this reduces cognitive overhead. You do not context-switch between workflow DSLs and application code.
Technical Verdict
Use Trigger.dev when:
- You are building agent workflows in TypeScript and want durable execution without learning Temporal
- You need queue-based concurrency control and retry boundaries out of the box
- You prefer managed infrastructure over operating your own task queue
- Your tasks run for minutes to hours, not days to weeks
Avoid Trigger.dev when:
- You need fine-grained activity-level retries and Temporal’s event sourcing model
- You require multi-language support (Temporal supports Go, Java, Python, PHP, .NET)
- You want full control over worker deployment and scaling
- Your workflows involve complex state machines or long-running sagas
Trigger.dev’s pivot from webhook automation to durable execution exposes a real gap in the TypeScript ecosystem. Temporal is powerful but heavy. Trigger.dev is opinionated but accessible. For agent orchestration, the simpler model often wins.