Trigger.dev V2 positions itself as a Temporal alternative for TypeScript developers who want durable execution without adopting Go runtimes or gRPC protocols. The pivot from “Zapier alternative” (V1) to workflow orchestration engine happened after 745 points and 190 comments on the initial Show HN revealed what developers actually needed: long-running tasks with retries, state persistence, and observability in their native language.
This matters for agentic workflows because agents need reliable background execution for tool calls, human-in-the-loop steps, and multi-stage pipelines. Temporal solves this with event sourcing in Go. Trigger.dev solves it by keeping everything in TypeScript and Node.js.
Architecture: How Durable Execution Works Without Event Sourcing
Trigger.dev runs tasks in isolated Node.js processes and persists execution state at explicit checkpoints. Instead of replaying an event log like Temporal, it saves snapshots of task progress to Postgres and resumes from the last successful step.
Key components:
- Task runtime: Isolated Node.js worker processes managed by the Trigger.dev platform
- State persistence: Postgres stores task state, retry metadata, and execution history
- Checkpoint API: Developers call
await checkpoint()to mark durable boundaries - Queue system: Tasks enter queues with concurrency limits and priority rules
- Retry engine: Exponential backoff with configurable max attempts, stored per task
The runtime does not use gRPC. Communication between the platform and workers happens over HTTP long polling or WebSockets. This removes the need for Protobuf schemas, code generation, and the operational complexity of maintaining gRPC endpoints.
Execution Model: Checkpoints vs. Event Replay
Temporal guarantees deterministic replay by re-executing workflow code from an event log. Trigger.dev guarantees resumption by saving explicit checkpoints.
export const researchAgent = task({
id: "research-agent",
run: async ({ topic }: { topic: string }) => {
// Step 1: Search (automatically checkpointed)
const searchResults = await search(topic);
// Step 2: Analyze (resumes here if Step 1 succeeded but Step 2 failed)
const analysis = await analyze(searchResults);
// Step 3: Generate report
const report = await generateReport(analysis);
return { report, stepsUsed: 3 };
}
});
Each await on a Trigger.dev primitive (like search, analyze, or generateReport) creates an implicit checkpoint. If the task crashes after search completes, the retry starts from analyze without re-running the search.
Trade-offs:
| Aspect | Temporal | Trigger.dev |
|---|---|---|
| Determinism | Full replay from event log | Checkpoint-based resumption |
| Language | Go workers, polyglot SDKs | TypeScript-native |
| State size | Unlimited (event sourcing) | Limited by checkpoint payload size |
| Debugging | Replay history in dev | Inspect checkpoints in Postgres |
| Deployment | Self-hosted or Temporal Cloud | Managed platform or self-hosted |
| gRPC dependency | Required | None |
Temporal’s replay model handles non-deterministic code (like random number generation) by storing decisions in the event log. Trigger.dev requires developers to avoid non-determinism between checkpoints or explicitly save random values to task state.
Retry and Failure Boundaries
Trigger.dev retries at the task level, not the workflow level. If a task fails, the platform re-enqueues it with exponential backoff. Developers configure retry behavior per task:
export const flakyScraper = task({
id: "scrape-with-retries",
retry: {
maxAttempts: 5,
factor: 2,
minTimeout: 1000,
maxTimeout: 60000,
},
run: async ({ url }) => {
const html = await fetch(url); // Retries if fetch throws
return parse(html);
}
});
Failure modes:
- Transient errors (network timeouts, rate limits): Automatic retry with backoff
- Permanent errors (invalid input, auth failure): Task moves to dead letter queue after max attempts
- Checkpoint corruption: Task restarts from the last valid checkpoint, not from scratch
- Worker crashes: Platform detects missing heartbeat and reschedules task on another worker
Temporal handles retries at the activity level and propagates failures up to the workflow. Trigger.dev treats each task as an independent unit of work. This simplifies reasoning about failure but makes it harder to coordinate multi-task workflows without explicit orchestration code.
Long-Running Tasks and Timeouts
Trigger.dev supports tasks that run for hours or days by decoupling execution from HTTP request lifecycles. The platform polls workers for heartbeats and kills tasks that exceed their configured timeout.
export const longRunningPipeline = task({
id: "data-pipeline",
timeout: "6h", // Task fails if it runs longer than 6 hours
run: async ({ dataset }) => {
for (const batch of dataset.batches) {
await processBatch(batch); // Each batch is checkpointed
await wait(60); // Sleep for 60 seconds between batches
}
}
});
Temporal uses timers and sleep activities to handle long delays. Trigger.dev uses await wait(seconds), which pauses the task and releases the worker. The platform resumes the task after the delay expires.
Concurrency control:
Trigger.dev queues tasks and limits how many run concurrently. Developers set concurrency per queue or per task:
export const rateLimitedAPI = task({
id: "call-external-api",
queue: {
name: "api-calls",
concurrencyLimit: 5, // Max 5 tasks running at once
},
run: async ({ endpoint }) => {
return await fetch(endpoint);
}
});
This prevents overwhelming downstream services without requiring external rate limiters or semaphore logic.
Observability: Tracing Without Distributed Tracing Infrastructure
Trigger.dev provides built-in observability through its dashboard. Every task execution generates:
- Execution timeline: Visual graph of checkpoints, retries, and wait periods
- Logs: Stdout/stderr from the task runtime
- Metrics: Duration, retry count, queue depth, concurrency usage
- Error traces: Stack traces from failed tasks
The platform does not require OpenTelemetry, Jaeger, or external tracing backends. All telemetry flows to the Trigger.dev API and appears in the web UI.
Limitations:
- No distributed tracing across multiple services (unless you instrument manually)
- Logs are stored in Postgres, not a dedicated log aggregator
- Metrics are scoped to Trigger.dev tasks, not the entire application
For teams already running Temporal, this is a downgrade in observability. For teams without tracing infrastructure, it’s a significant improvement over ad-hoc logging.
Deployment Shape: Managed Platform vs. Self-Hosted
Trigger.dev offers two deployment models:
- Managed platform: Tasks run on Trigger.dev’s infrastructure. You push code, they handle scaling, retries, and persistence.
- Self-hosted: Run the open-source runtime in your own Kubernetes cluster or VMs. You manage Postgres, worker pools, and upgrades.
The managed platform uses isolated Node.js containers per task. Each container has a CPU and memory limit. Tasks that exceed limits are killed and retried.
Self-hosting requires:
- Postgres 14+ for state persistence
- Redis for queue management
- Worker nodes running the Trigger.dev runtime
- Load balancer for HTTP/WebSocket traffic
Temporal requires a similar stack (Postgres/Cassandra, worker pools, frontend service) but adds complexity around gRPC load balancing and versioning workflows.
When to Use Trigger.dev vs. Temporal
Use Trigger.dev when:
- Your team writes TypeScript and wants to avoid polyglot SDKs
- You need durable execution but not full event sourcing
- You want managed infrastructure without operating a distributed system
- Your workflows fit within checkpoint payload limits (typically a few MB)
- You prioritize developer experience over maximum flexibility
Avoid Trigger.dev when:
- You need deterministic replay for non-deterministic code
- Your workflows generate gigabytes of state between steps
- You require multi-language workers (Python, Go, Java)
- You already operate Temporal and have workflows in production
- You need advanced features like versioning, signals, or child workflows
Trigger.dev competes with Temporal by narrowing scope. It does not try to be a general-purpose workflow engine. It targets TypeScript developers who want reliable background tasks without learning Go or gRPC.
Technical Verdict
Trigger.dev succeeds by making durable execution accessible to TypeScript developers. The checkpoint model is easier to reason about than event sourcing, and the managed platform removes operational burden. The trade-off is less flexibility: you cannot replay arbitrary code, and state size is bounded.
For agentic workflows, Trigger.dev works well when agents call external APIs, wait for human input, or process data in stages. It struggles with workflows that need complex branching, versioning, or tight integration with non-TypeScript services.
If you are building agents in TypeScript and need retries, queues, and observability without standing up Temporal, Trigger.dev is worth evaluating. If you need the full power of event sourcing or already have Temporal expertise, stick with Temporal.