mech.app
Automation

Trigger.dev V2: What a Temporal Alternative for TypeScript Reveals About Durable Execution Plumbing

How Trigger.dev pivoted from webhook orchestration to durable execution primitives, comparing queue architecture, retry semantics, and state persistence.

Source: trigger.dev
Trigger.dev V2: What a Temporal Alternative for TypeScript Reveals About Durable Execution Plumbing

Trigger.dev started as a “developer-first Zapier alternative” in February 2023 (745 points on HN). By October 2023, the team shipped V2 and repositioned as a “Temporal alternative for TypeScript devs” (172 points). That pivot tells something important: developers wanted durable execution primitives, not just webhook glue.

The shift exposes a gap in the orchestration landscape. Temporal gives workflow engines with event sourcing and replay semantics, but teams pay for JVM overhead and a steep learning curve. Trigger.dev bets that TypeScript developers want task queues with retries, observability, and state persistence without running a cluster.

What Changed Between V1 and V2

V1 focused on event-driven workflows. Teams connected third-party webhooks (Stripe, GitHub, Slack) and wrote handlers in TypeScript. The runtime managed subscriptions and delivery guarantees, but the execution model was stateless. If a task failed halfway through, context was lost.

V2 introduced durable execution:

  • Task primitives replace event handlers. Each task is a function with automatic retries, timeouts, and idempotency.
  • State persistence across retries. If a task crashes after step 3 of 10, the next retry resumes at step 4.
  • Queue-based scheduling instead of webhook subscriptions. Tasks are enqueued programmatically, and workers pull from queues with concurrency controls.
  • Observability hooks for tracing execution, failures, and queue depth in real time.

The architecture now looks more like BullMQ or Inngest than Zapier.

Queue Architecture and Retry Semantics

Trigger.dev uses a persistent task queue backed by a relational database. Each task is stored with status, retry count, scheduled time, and execution context. Workers poll the queue and claim tasks atomically to prevent duplicate execution.

Retry logic is configurable per task:

// Durable task with exponential backoff retry policy
export const processVideo = task({
  id: "process-video",
  retry: {
    maxAttempts: 5,
    factor: 2,
    minTimeout: 1000,
    maxTimeout: 60000,
  },
  run: async ({ videoUrl }: { videoUrl: string }) => {
    const file = await downloadVideo(videoUrl);
    const transcoded = await transcodeVideo(file);
    await uploadToS3(transcoded);
    return { s3Key: transcoded.key };
  },
});

If transcodeVideo throws, the task goes back to the queue with exponential backoff. The next retry starts from scratch because Trigger.dev does not checkpoint mid-function. This differs from Temporal, where workflows replay from event history.

Idempotency requires manual implementation. Developers pass an idempotencyKey when enqueueing a task. Trigger.dev deduplicates based on that key, but task logic must be written to handle retries safely if side effects can occur.

State Persistence Model

The platform persists task state in a relational database. This storage layer tracks task definitions, execution context (input payload, output, error stack), and run metadata (start time, end time, retry count, worker ID).

When a task retries, the worker fetches the stored state, deserializes the input payload, and re-executes the function. There is no replay of intermediate steps. If a task has 10 API calls and fails on call 9, the retry makes all 10 calls again.

This is simpler than Temporal’s event sourcing but less efficient for long-running workflows with expensive steps. Teams can mitigate this by breaking tasks into smaller units and chaining them with task.trigger().

Deployment Topology

Trigger.dev runs as a hosted service or self-hosted. The hosted version manages workers, database infrastructure, and observability tooling. Self-hosted deployments require:

  • Database instance for task queue and metadata
  • Worker processes that poll the queue and execute tasks
  • API server for enqueueing tasks and serving the dashboard

Workers are Node.js processes that import task definitions. Horizontal scaling is achieved by running more workers. Each worker claims tasks from the queue with row-level locks, so there is no coordination overhead.

For long-running tasks, Trigger.dev isolates execution in separate processes. Developers configure maxDuration per task. If a task exceeds the limit, the worker kills the process and marks the task as failed. This prevents memory leaks and runaway tasks from blocking the queue.

Observability and Monitoring

The dashboard shows:

  • Task runs: status, duration, retry count, error logs
  • Queue depth: number of pending tasks per queue
  • Worker health: active workers, tasks in progress, CPU/memory usage

Task execution can be traced with OpenTelemetry. Trigger.dev emits spans for task start, end, retry, and failure. Spans can be sent to Datadog, Honeycomb, or any OTLP-compatible backend.

For debugging, the dashboard includes a replay feature. Developers can re-run a failed task with the same input payload and inspect logs in real time. Replay does not re-execute side effects that were already committed (emails sent, database writes), making it safe for inspecting transient failures like rate limits or network errors.

Comparison: Trigger.dev vs. Temporal

DimensionTrigger.devTemporal
Execution modelTask queue with retriesWorkflow engine with event sourcing
State persistenceDatabase row per taskEvent history in Cassandra/Postgres
Retry semanticsRe-execute entire functionReplay from last checkpoint
Language supportTypeScript onlyGo, Java, Python, TypeScript
DeploymentHosted or self-hosted (Node.js + database)Self-hosted cluster (JVM + database)
Learning curveLow (familiar task queue API)High (workflow DSL, activity patterns)
ObservabilityBuilt-in dashboard + OpenTelemetryTemporal UI + custom exporters
IdempotencyManual implementation requiredAutomatic via deterministic replay

Trigger.dev is simpler to deploy and reason about. Developers write async functions, configure retries, and enqueue tasks. Temporal gives stronger guarantees (deterministic replay, automatic checkpointing) but requires understanding workflows, activities, and signals.

Failure Modes and Operational Risks

Task retries can amplify side effects. If a task sends an email and fails after sending, the retry sends another email. Implementers must add idempotency checks (e.g., verify if email was already sent before sending again).

Long-running tasks block workers. If a task runs for 10 minutes, that worker cannot process other tasks. Mitigation strategies include increasing worker count or breaking tasks into smaller units.

Database becomes a bottleneck. The task queue is stored in a single database table. High-throughput workloads (thousands of tasks per second) can saturate the database. The platform recommends partitioning the table or using read replicas for observability queries.

No cross-task transactions. If coordination is needed between multiple tasks (e.g., task A and task B must both succeed or both fail), saga patterns must be implemented manually. Temporal handles this with workflow guarantees.

When to Use Trigger.dev

Use Trigger.dev when projects need:

  • Durable task execution without running a Temporal cluster
  • TypeScript-native workflows with minimal boilerplate
  • Hosted infrastructure to avoid managing database or workers
  • Simple retry semantics where re-executing the entire function is acceptable

Avoid Trigger.dev when projects need:

  • Deterministic replay for long-running workflows with expensive steps
  • Cross-language support (Trigger.dev is TypeScript only)
  • Transactional guarantees across multiple tasks without manual saga logic
  • High-throughput task queues (tens of thousands of tasks per second)

Technical Verdict

Trigger.dev fills the gap between BullMQ (low-level task queue) and Temporal (full workflow engine). It provides durable execution with retries, observability, and state persistence without the operational complexity of a Temporal cluster.

The trade-off is efficiency. Re-executing entire functions on retry is simpler than event sourcing but wasteful for workflows with many steps. If tasks are short-lived (seconds to minutes) and side effects are idempotent, Trigger.dev is a good fit. If workflows are long-running with checkpointing and deterministic replay requirements, Temporal is worth the complexity.

For TypeScript teams building AI agents, background jobs, or scheduled workflows, Trigger.dev offers a pragmatic middle ground. Teams get durable execution without learning a new DSL or running a JVM cluster.