Trigger.dev launched in February 2023 as a “developer-first Zapier alternative” and earned 745 points on Hacker News. Eight months later, the team shipped V2 with a different pitch: a Temporal alternative for TypeScript developers. That pivot exposes a critical gap in the durable execution landscape and reveals what happens when you build workflow infrastructure for the language most developers actually use.
The shift was not cosmetic. V2 replaced event-driven webhook chaining with durable execution primitives: automatic retries, state checkpointing, and long-running task orchestration. The team discovered that developers did not want another IFTTT clone. They wanted a way to run multi-hour jobs, recover from transient failures, and debug workflows without learning Go or deploying a Temporal cluster.
Why Temporal Is Hard and Why That Matters
Temporal is the gold standard for durable execution. It guarantees that workflows complete even if workers crash, networks partition, or third-party APIs timeout. The trade-off is operational complexity:
- Go-first architecture: The SDK is built for Go. TypeScript and Python SDKs exist but feel like afterthoughts.
- Cluster management: You run Temporal Server (Cassandra or PostgreSQL backend), workers, and a web UI. Self-hosting means managing stateful services.
- Workflow determinism: You cannot call
Math.random()orDate.now()inside a workflow. Side effects must be wrapped in activities. Violating this breaks replay.
For teams with SRE capacity and Go expertise, Temporal is worth it. For a solo developer building an AI agent that scrapes websites and waits for human approval, the learning curve is a blocker.
What Trigger.dev V2 Changed
V2 kept the durable execution guarantees but optimized for TypeScript ergonomics and managed infrastructure. In practice, this looks like:
State Persistence Without Manual Checkpoints
Temporal requires you to structure workflows as deterministic functions. Trigger.dev uses a task-based model where each step is automatically checkpointed. If a task fails, the runtime replays from the last successful step.
export const processOrder = task({
id: "process-order",
run: async ({ orderId }: { orderId: string }) => {
// Step 1: Fetch order (checkpointed)
const order = await db.orders.findUnique({ where: { id: orderId } });
// Step 2: Charge payment (checkpointed)
const charge = await stripe.charges.create({
amount: order.total,
currency: "usd",
source: order.paymentToken,
});
// Step 3: Send confirmation (checkpointed)
await sendEmail({
to: order.email,
template: "order-confirmation",
data: { orderId, chargeId: charge.id },
});
return { status: "completed", chargeId: charge.id };
},
});
If the Stripe API times out, the task retries from step 2. The database fetch does not re-run. The runtime tracks execution state in a managed Postgres instance, so you do not provision or manage storage infrastructure directly (though storage costs are billed separately).
Retry Semantics and Failure Modes
Trigger.dev uses exponential backoff with jitter by default. You can override per task:
export const flakyScraper = task({
id: "scrape-site",
retry: {
maxAttempts: 5,
factor: 2,
minTimeout: 1000,
maxTimeout: 60000,
randomize: true,
},
run: async ({ url }: { url: string }) => {
const response = await fetch(url);
const html = await response.text();
return parseHTML(html);
},
});
Failed tasks land in a dead-letter queue visible in the dashboard. You can manually retry, inspect logs, or trigger a webhook. There is no built-in circuit breaker, so if an upstream API is down for hours, tasks will exhaust retries and fail.
Long-Running Tasks and Timeouts
Temporal workflows can run for years. Trigger.dev tasks have a 24-hour wall-clock limit on the managed tier. For longer jobs, you chain tasks:
import { addWeeks } from "date-fns";
export const weeklyReport = task({
id: "weekly-report",
run: async ({ startDate }: { startDate: Date }) => {
const data = await aggregateData(startDate);
// Trigger next week's report
await weeklyReport.trigger({
startDate: addWeeks(startDate, 1),
});
return data;
},
});
This works for scheduled jobs but breaks the “single workflow” mental model. If you need true multi-day orchestration, you still need Temporal.
Deployment Model and Observability
Trigger.dev runs tasks in managed workers (Node.js or Bun runtimes). You push code via CLI, and the platform handles scaling. The architecture looks like this:
| Component | Responsibility | Failure Mode |
|---|---|---|
| API Server | Accepts task triggers, enqueues jobs | Rate limits apply; overflow queued to durable storage |
| Worker Pool | Executes tasks, reports checkpoints | Auto-scales to zero; cold starts add 2-5s latency |
| State Store | Postgres for execution logs, retries | Managed RDS; no user access to raw tables |
| Dashboard | Real-time logs, traces, manual retries | Read-only; no SSH or direct DB queries |
Observability is built in. Every task gets:
- Structured logs with trace IDs
- Step-by-step execution timeline
- Retry history and failure reasons
- OpenTelemetry spans (exportable to Datadog, Honeycomb)
Direct SSH access and debugger attachment are not supported. For complex failures, you add logging and redeploy.
TypeScript vs. Go: Developer Experience Trade-offs
Temporal’s Go SDK enforces determinism at compile time. Trigger.dev’s TypeScript SDK relies on runtime checks and developer discipline. Here is what that means:
Temporal (Go):
- Workflows are pure functions. Side effects live in activities.
- Replay is guaranteed safe because the compiler prevents non-deterministic code.
- Debugging requires understanding workflow history and event sourcing.
Trigger.dev (TypeScript):
- Tasks can call APIs directly. The runtime checkpoints after each
await. - Non-deterministic code (like
Math.random()) works but breaks replay if you are not careful. - Debugging uses standard
console.logand stack traces.
For teams building AI agents that call LLMs, scrape websites, and wait for webhooks, the TypeScript model is faster to ship. For financial systems that require audit trails and formal verification, Temporal’s guarantees matter more.
When Self-Hosting Makes Sense
Trigger.dev offers a self-hosted option (Docker Compose or Kubernetes). You run:
- API server (Node.js)
- Worker coordinator (manages task distribution)
- Postgres (state and logs)
- Redis (job queue)
This makes sense if:
- You process sensitive data that cannot leave your VPC
- You need sub-second task latency (managed workers have cold start overhead)
- You want to customize retry logic or add circuit breakers
The trade-off is operational burden. You manage database backups, worker scaling, and security patches. The managed tier costs $20/month for 500,000 task runs. Self-hosting costs engineer time.
Comparison: Trigger.dev vs. Temporal vs. Inngest
| Feature | Trigger.dev V2 | Temporal | Inngest |
|---|---|---|---|
| Primary Language | TypeScript | Go (TS/Python SDKs exist) | TypeScript |
| State Persistence | Managed Postgres | Self-hosted (Cassandra/Postgres) | Managed (proprietary) |
| Max Task Duration | 24 hours | Unlimited | 24 hours |
| Retry Strategy | Exponential backoff, DLQ | Configurable, infinite retries | Exponential backoff, manual replay |
| Deployment | Managed workers or self-hosted | Self-hosted cluster required | Managed only |
| Observability | Built-in dashboard, OTEL export | Temporal UI, requires setup | Built-in dashboard |
| Pricing | $20/mo for 500k runs | Free (self-hosted), enterprise for cloud | Free tier, paid plans vary |
Likely Failure Modes
Cold Start Latency: Managed workers spin down after inactivity. First task in a batch adds 2-5 seconds. If you trigger 1000 tasks simultaneously, some will queue.
24-Hour Limit: Tasks that need to wait days (like “send reminder in 3 days”) require chaining or external schedulers. Temporal handles this natively.
No Circuit Breaker: If an upstream API is down, tasks retry until max attempts. You cannot pause all tasks for a specific integration without custom logic.
Vendor Lock-In: The managed tier uses proprietary state storage. Migrating to Temporal or self-hosted Trigger.dev requires rewriting task definitions.
Technical Verdict
Use Trigger.dev V2 when:
- You build in TypeScript and want durable execution without learning Go
- Tasks complete in hours, not days
- You prefer managed infrastructure over running a Temporal cluster
- You need fast iteration on AI agents, webhooks, or background jobs
Avoid it when:
- You need multi-day workflows with human-in-the-loop steps
- You require formal guarantees about determinism (financial systems, compliance)
- You already run Temporal and have Go expertise
- You need sub-second task latency at scale
Trigger.dev is not a Temporal replacement. It is a TypeScript-first durable execution runtime that trades some of Temporal’s guarantees for developer velocity. For teams building AI agents, the trade-off is worth it. For teams building payment systems, it is not.