mech.app
Automation

Trigger.dev V2: TypeScript Workflow Engine Architecture Without Temporal's Go Runtime

How Trigger.dev persists state, handles retries, and isolates execution for durable TypeScript workflows without event sourcing or Go dependencies.

Source: trigger.dev
Trigger.dev V2: TypeScript Workflow Engine Architecture Without Temporal's Go Runtime

Trigger.dev launched in February 2023 as a Zapier alternative for developers (745 points on HN). Eight months later, the team shipped V2 with a complete architectural pivot: durable execution primitives for long-running TypeScript workflows. The shift came from user feedback. Developers wanted retry guarantees, state persistence, and scheduling without learning Go or deploying Temporal clusters.

The V2 architecture competes directly with Temporal by offering workflow durability in a TypeScript-native runtime. No event sourcing model. No separate worker pools in Go. The tradeoff is different failure semantics and a managed execution model that hides some of the control Temporal exposes.

State Persistence Without Event Sourcing

Temporal rebuilds workflow state by replaying a deterministic event log. Trigger.dev takes a checkpoint approach:

  • Each task execution writes state snapshots to Postgres at explicit await boundaries
  • Retries resume from the last successful checkpoint, not from the beginning
  • State includes serialized function arguments, return values, and execution context
  • No replay required, but you lose the audit trail of every decision

This means faster recovery after transient failures but less visibility into why a workflow made a specific choice three retries ago. If your task mutates external state (writes to a database, sends an email), you must handle idempotency yourself. Temporal’s event log makes it easier to reason about side effects because you can see every attempt.

Execution Isolation Model

Trigger.dev runs customer code in isolated Node.js processes, not containers or V8 isolates:

  • Each task gets a dedicated worker process with resource limits (CPU, memory)
  • Processes are ephemeral and torn down after execution completes
  • No shared memory between tasks, even from the same project
  • Crash isolation is process-level, so a segfault in one task does not affect others

This is simpler than Temporal’s worker pool model but less flexible. You cannot bring your own runtime or sidecar services. The process boundary also means cold start overhead on every invocation, though Trigger.dev mitigates this with a warm pool for frequently called tasks.

Concurrency and Scheduling Primitives

Trigger.dev exposes three scheduling mechanisms:

  1. Event triggers: HTTP webhooks or SDK calls that start a task immediately
  2. Cron schedules: Durable timers that survive restarts, stored in Postgres
  3. Queues with concurrency limits: Named queues with max-in-flight controls

Backpressure is handled at the queue level. If a queue hits its concurrency limit, new tasks wait in Postgres until a slot opens. No separate message broker. This keeps the architecture simple but limits throughput compared to systems that use Redis or Kafka for queueing.

export const processVideo = task({
  id: "process-video",
  queue: { name: "video", concurrency: 5 },
  run: async (payload: { videoUrl: string }) => {
    const download = await fetch(payload.videoUrl);
    const buffer = await download.arrayBuffer();
    
    // Checkpoint happens here automatically
    const transcoded = await transcode(buffer); // Simulated long-running operation
    
    // Another checkpoint
    await storage.upload(transcoded);
    
    return { status: "complete" };
  }
});

Each await in the task body is a potential checkpoint. If the process crashes after transcoding but before upload, the retry starts from the upload step. The transcoded buffer is serialized and restored from Postgres.

Observability and Debugging

Trigger.dev provides structured logs and trace IDs for every task execution:

  • Logs are tagged with taskId, runId, and attemptNumber
  • Trace IDs propagate through nested task calls
  • The dashboard shows execution timelines with checkpoint markers
  • No replay capability, so debugging requires reading logs and state snapshots

You cannot step through a workflow execution like you can with Temporal’s replay feature. If a task fails intermittently, you see the error and the state at the last checkpoint, but not the sequence of decisions that led there.

Versioning and Mid-Flight Changes

When you deploy a new version of a task definition, Trigger.dev uses a simple rule:

  • In-flight executions continue with the old code
  • New executions use the new code
  • No automatic migration or compatibility checks

This avoids Temporal’s workflow versioning complexity but creates a deployment hazard. If your new code expects a different state shape, old executions will fail when they resume. You must handle schema evolution manually, usually by checking a version field in the task payload.

FeatureTrigger.dev V2Temporal
State modelCheckpoint snapshotsEvent sourcing replay
Language supportTypeScript onlyGo, Java, Python, PHP, .NET
Execution isolationNode.js processesWorker pools (any runtime)
Cold start / warm poolsProcess-level, mitigated by warm poolWorker pool reuse
ObservabilityLogs + trace IDsReplay + event history
Deployment modelManaged platformSelf-hosted or cloud
VersioningManual schema checksBuilt-in workflow versioning

Retry and Failure Modes

Trigger.dev retries failed tasks with exponential backoff:

  • Default retry policy: 3 attempts with 2x backoff starting at 1 second
  • Configurable per task with max attempts and backoff multiplier
  • Retries resume from the last checkpoint, not from the start
  • Poison pill tasks (always fail) eventually move to a dead letter queue

The checkpoint model means you can lose work if a task crashes between checkpoints. If your task does expensive computation before the next await, that work is lost on retry. Temporal’s replay model avoids this because every decision is logged, but at the cost of determinism constraints (no random numbers, no wall clock reads).

Technical Verdict

Trigger.dev’s checkpoint model is faster to recover from transient failures but sacrifices the deterministic replay guarantees that make Temporal suitable for financial workflows or compliance-heavy systems. The architectural win is clear: process isolation plus Postgres checkpoints equals simpler operations with no event sourcing complexity. You get workflow durability without running a distributed system or learning Go.

The loss is equally specific. You have no audit trail of intermediate decisions. If a task retries three times and succeeds on the fourth attempt, you see the final state and error logs, but not the branching logic or external API responses that led to each failure. You must handle idempotency yourself because Trigger.dev does not track which side effects already executed.

This architecture fits workflows with clear I/O boundaries. A media processing pipeline that downloads a file, transcodes it, and uploads the result works well because each await is a natural checkpoint. The transcoding step is expensive but deterministic given the input buffer. If the process crashes after transcoding, the retry skips the download and reuses the serialized buffer.

It does not fit workflows with long-running compute phases that mutate external state. Imagine a task that scrapes 100 web pages, processes them in memory, and writes results to a database. If the task crashes after scraping 50 pages but before the next await, the retry starts from the beginning. You either duplicate writes to the database (bad) or add complex deduplication logic (worse). Temporal’s event log would record each scrape decision, letting you resume from page 51.

The managed platform model also matters. Trigger.dev handles scaling, monitoring, and infrastructure. You pay for execution time, not for idle worker capacity. This is a win for teams that want to ship workflows without hiring SREs. It is a loss for teams that need custom runtimes, sidecar services, or tight cost control over self-hosted infrastructure.

When to Use Trigger.dev

Good fit:

  • TypeScript-first teams that want workflow durability without learning Go
  • Tasks with clear checkpoint boundaries (API calls, database writes)
  • Projects that benefit from managed infrastructure over self-hosted clusters
  • Workflows where audit trails are less critical than fast recovery
  • Teams optimizing for developer velocity over infrastructure control

Avoid if:

  • You need multi-language support or custom runtimes
  • Workflow replay and detailed audit logs are compliance requirements
  • You already run Temporal and need to integrate with existing workflows
  • Your tasks have long compute phases between I/O operations (checkpoint loss risk)
  • You require fine-grained cost control or bring-your-own infrastructure

The V2 pivot from Zapier-style integrations to durable execution shows product-market fit for TypeScript workflow orchestration. The architecture trades Temporal’s flexibility and audit guarantees for simplicity and language-native ergonomics. If your team lives in TypeScript and your workflows fit the checkpoint model, Trigger.dev removes the operational burden of running a distributed workflow engine.