mech.app
Automation

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

How Trigger.dev pivoted from Zapier-style event triggers to durable execution, exposing the architectural differences between workflow orchestration and...

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

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 await boundary 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

AspectTemporalTrigger.dev
Programming modelWorkflows + Activities (separate concerns)Single task function with checkpoints
State machineExplicit workflow DSLImplicit from async/await
Language supportGo, Java, TypeScript, Python, PHPTypeScript only
Execution guaranteesExactly-once via deterministic replayAt-least-once with idempotency keys
Timeout handlingInfinite workflow duration, activity timeoutsServerless escape hatch (no hard limits)
Operational complexitySelf-hosted cluster (Cassandra, workers, frontend)Managed platform or Docker Compose
ObservabilityTemporal UI, event historyTrigger.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:

  1. Checkpoint serialization errors: If your task state includes non-serializable objects (class instances, functions), checkpoints fail. Solution: use plain objects and primitives.

  2. Postgres connection exhaustion: High-concurrency workloads can saturate connection pools. Solution: tune pool size or shard by queue.

  3. Non-deterministic code: If your task reads Date.now() or Math.random() across checkpoints, replays produce different results. Solution: pass timestamps/seeds as payload.

  4. Memory leaks in long-running tasks: Workers don’t restart between checkpoints. Solution: monitor heap usage, restart workers periodically.

  5. 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.