Trigger.dev launched V1 as a Zapier alternative for developers. After 745 points on Hacker News and months of user feedback, the team pivoted to V2: a durable execution engine for TypeScript that competes with Temporal without requiring worker pools, external databases, or Kubernetes clusters.
The shift matters for agent builders. Temporal gives you workflow guarantees but demands operational overhead. Trigger.dev promises the same durability using serverless primitives and managed state persistence. The V2 Show HN drew 172 points and 39 comments, signaling real interest in TypeScript-native orchestration that doesn’t force you to run infrastructure.
What Changed Between V1 and V2
V1 focused on webhook triggers and pre-built integrations. V2 exposes durable execution primitives:
- Task definitions that survive process crashes and retries
- State persistence without requiring Redis or Postgres
- Scheduled jobs with cron syntax and no timeout limits
- Concurrency controls and queue management
- Observability hooks for tracing and monitoring
The pivot came from feedback. Developers wanted long-running workflows, not just event handlers. They wanted retries that actually worked, not best-effort HTTP callbacks.
Durable Execution Model
Trigger.dev tasks are functions wrapped in a task() call. The runtime intercepts execution, checkpoints state, and handles retries automatically.
export const processOrder = task({
id: "process-order",
retry: {
maxAttempts: 5,
factor: 2,
minTimeout: 1000,
maxTimeout: 60000,
},
run: async (payload: { orderId: string }) => {
const order = await fetchOrder(payload.orderId);
// State persisted here
await chargePayment(order.paymentMethod, order.total);
// If this fails, retry from checkpoint
await sendConfirmationEmail(order.email);
return { status: "complete", orderId: order.id };
},
});
The runtime serializes task state after each await. If the process crashes or times out, the next execution resumes from the last checkpoint. You don’t write retry logic or manage state stores.
State Persistence Without External Databases
Trigger.dev persists task state to its managed backend. Each task execution gets a unique run ID. The runtime stores:
- Function arguments
- Return values from each await
- Retry attempt count
- Execution timeline
When a task retries, the runtime replays the function from the last successful checkpoint. This avoids re-executing idempotent steps like payment charges.
Temporal uses a similar model but requires you to run worker processes that poll for tasks. Trigger.dev runs tasks in ephemeral containers and stores state server-side. You deploy code, not infrastructure.
Serverless Execution Shape
Tasks run in isolated containers managed by Trigger.dev’s platform. Each invocation:
- Pulls the latest task code from your deployment
- Spins up a container with the Node.js or Bun runtime
- Executes the task function
- Checkpoints state after each await
- Tears down the container on completion or failure
Cold starts add latency (typically 200-500ms). For agent workflows that run for minutes or hours, this is negligible. For high-frequency tasks (thousands per second), cold starts become a bottleneck.
Concurrency limits prevent runaway costs. You set max concurrent executions per task. The platform queues excess invocations and processes them as capacity frees up.
Comparison: Trigger.dev vs. Temporal
| Dimension | Trigger.dev V2 | Temporal |
|---|---|---|
| Runtime | Serverless containers | Self-hosted workers |
| State storage | Managed backend | Cassandra/Postgres |
| Deployment | Git push | Worker binary + config |
| Cold start | 200-500ms | None (long-running workers) |
| Retry model | Exponential backoff, checkpointing | Workflow history replay |
| Concurrency | Platform-managed queues | Worker pool size |
| Observability | Built-in dashboard | Requires external tracing |
| Cost model | Per-execution + duration | Infrastructure + ops labor |
Temporal wins on throughput and latency. Trigger.dev wins on operational simplicity and zero-infrastructure deployments.
Scheduled Tasks and Cron
Trigger.dev supports durable cron schedules without timeout limits. Traditional cron jobs fail if the process crashes mid-execution. Trigger.dev persists state and resumes from the last checkpoint.
export const dailyReport = task({
id: "daily-report",
schedule: "0 9 * * *", // 9 AM daily
run: async () => {
const data = await fetchAnalytics();
const report = await generatePDF(data);
await emailReport(report);
},
});
The scheduler guarantees at-least-once execution. If a task runs longer than the schedule interval, the next invocation waits until the current one completes.
Concurrency and Queue Management
You control how many instances of a task run concurrently:
export const processVideo = task({
id: "process-video",
queue: {
concurrencyLimit: 5,
},
run: async (payload: { videoUrl: string }) => {
// Only 5 videos process at once
},
});
Excess invocations queue in FIFO order. The platform tracks queue depth and execution time in the dashboard. This prevents resource exhaustion when processing large batches.
Observability and Tracing
Each task execution generates a trace with:
- Start and end timestamps
- Checkpoint intervals
- Retry attempts
- Error stack traces
- Return values
The dashboard shows real-time task status, queue depth, and failure rates. You can replay failed executions to debug state corruption or transient errors.
Temporal requires external tracing (Jaeger, Zipkin) and custom instrumentation. Trigger.dev bundles observability into the runtime.
Agent Workflow Patterns
Trigger.dev fits multi-step agent workflows where each step might fail independently. Example: research agent with tool calls.
export const researchAgent = task({
id: "research-agent",
run: async ({ topic }: { topic: string }) => {
const messages: CoreMessage[] = [
{ role: "user", content: `Research: ${topic}` },
];
for (let i = 0; i < 10; i++) {
const { text, toolCalls } = await generateText({
model: anthropic("claude-opus-4"),
messages,
tools: { search, browse, analyze },
maxSteps: 5,
});
if (!toolCalls.length) {
return { summary: text };
}
for (const call of toolCalls) {
const result = await executeTool(call);
messages.push({ role: "tool", content: result });
}
}
},
});
If executeTool() fails (rate limit, timeout), the task retries from the last successful tool call. The LLM doesn’t re-generate text or re-execute prior tools.
Failure Modes and Limits
Cold start latency: First invocation of a task after deployment takes 200-500ms. Subsequent invocations reuse warm containers when possible.
State size limits: Task state is serialized and stored. Large payloads (multi-MB) slow down checkpointing and replay. Keep state lean or store blobs externally.
Retry exhaustion: After max retry attempts, the task moves to a dead-letter queue. You must manually inspect and re-trigger failed runs.
Concurrency bottlenecks: If queue depth grows faster than tasks complete, latency increases. Monitor queue metrics and adjust concurrency limits.
Vendor lock-in: State persistence and runtime are Trigger.dev-managed. Migrating to self-hosted Temporal requires rewriting task definitions.
Deployment and Self-Hosting
Trigger.dev offers a managed cloud platform. You push code via Git or CLI, and the platform handles container builds and deployments.
Self-hosting is possible using the open-source repository. You run the Trigger.dev server (Node.js), a Postgres database for task state, and a container runtime (Docker or Kubernetes). This removes vendor lock-in but reintroduces operational overhead.
Security Boundaries
Tasks run in isolated containers with no shared state. Each execution gets a fresh environment. Secrets are injected via environment variables, not hardcoded in task code.
The platform enforces rate limits and concurrency caps to prevent abuse. Multi-tenant workloads are isolated at the container level, not just process level.
For sensitive workflows, self-hosting gives you full control over data residency and network boundaries.
Technical Verdict
Use Trigger.dev when:
- You want durable execution without running infrastructure
- Your workflows are TypeScript-native and fit serverless execution
- Cold starts under 500ms are acceptable
- You need built-in observability and retry logic
- You’re building agent workflows with multi-step tool calls
Avoid Trigger.dev when:
- You need sub-100ms task latency
- Your workflows process thousands of tasks per second
- You require full control over state storage and replay logic
- You already run Temporal and can’t justify migration costs
- Your tasks generate multi-MB state payloads
Trigger.dev trades throughput and control for operational simplicity. It’s a strong fit for agent builders who want workflow guarantees without Kubernetes clusters. For high-frequency, low-latency orchestration, Temporal remains the better choice.