Trigger.dev launched in February 2023 as a Zapier alternative, then pivoted eight months later to compete with Temporal. The V2 architecture targets TypeScript developers who need durable background jobs without running a workflow engine. This shift exposes a practical question for agent builders: when do you need event sourcing and replay semantics, and when does a managed job queue suffice?
The distinction matters because agent tasks often span minutes or hours, call external APIs that fail unpredictably, and require state that survives process crashes. Temporal solves this with deterministic replay. Trigger.dev solves it with persistent task state and automatic retries. The plumbing differs enough that choosing wrong costs you weeks of refactoring.
Execution Model: Replay vs. Persistent State
Temporal workflows are deterministic functions. Every decision, timer, and activity call gets logged to an event history. When a worker crashes, Temporal replays the entire workflow from the event log. This guarantees exactly-once semantics and lets you time-travel through execution state.
Trigger.dev tasks are TypeScript functions that checkpoint their state to a database. When a task pauses (waiting for an external API, sleeping, or hitting a rate limit), the runtime serializes the execution context and stores it. On resume, it deserializes and continues. No replay, no event sourcing.
Key differences:
- Temporal requires deterministic code (no random UUIDs, no direct API calls in workflow logic). Activities handle side effects.
- Trigger.dev lets you write imperative code with side effects directly in task functions. The runtime handles idempotency through checkpointing.
- Temporal’s replay model catches non-determinism bugs at runtime. Trigger.dev’s model assumes your code is idempotent or uses the provided primitives (
task.wait,task.retry).
For agent workflows that orchestrate LLM calls, tool invocations, and human-in-the-loop steps, Trigger.dev’s approach feels more natural. You write async/await code without splitting logic into workflows and activities.
State Persistence and Durability
Temporal stores workflow state in its own database (Cassandra, PostgreSQL, or MySQL). The workflow history grows with every decision. Large workflows can hit history size limits (50 MB default), forcing you to use continue-as-new patterns (a workflow spawns a new instance to reset history size).
Trigger.dev stores task state in your database (PostgreSQL or managed cloud storage). Each checkpoint writes the serialized execution context. The state size limit depends on your database configuration, but the architecture avoids unbounded growth because tasks complete and their state gets archived.
Checkpoint granularity:
Trigger.dev checkpoints at explicit await points: API calls, sleeps, waits for external events. You control when state gets persisted. Temporal checkpoints every decision, which provides finer recovery granularity but increases storage overhead.
For multi-step agent tasks (research, summarize, generate, review), Trigger.dev’s explicit checkpoints map cleanly to task boundaries. You know exactly where execution can resume.
Retry Semantics and Failure Handling
Temporal retries activities automatically. You configure retry policies (max attempts, backoff, non-retryable errors) per activity. Workflows themselves don’t retry; they either complete or fail permanently.
Trigger.dev retries tasks automatically with exponential backoff. You configure retries per task or globally. The runtime distinguishes between transient errors (network timeouts, rate limits) and permanent failures (invalid input, authorization errors).
Error propagation:
- Temporal workflows can catch activity failures and implement custom compensation logic. You write explicit error handling in workflow code.
- Trigger.dev tasks can catch errors in try/catch blocks. The runtime provides
task.onFailurehooks for cleanup logic.
For agent workflows, Temporal’s compensation model shines when you need to undo partial work (refund a payment, delete a created resource). Trigger.dev’s simpler model works when retries or manual intervention suffice.
Deployment Boundaries and Isolation
Temporal requires running a server cluster (frontend, history, matching, worker services). Workers poll task queues and execute workflow/activity code. You deploy workers separately from the Temporal cluster.
Trigger.dev V2 offers two deployment models:
- Managed cloud: Tasks run in isolated containers on Trigger.dev’s infrastructure. You push code, they handle scaling.
- Self-hosted: Run the Trigger.dev coordinator and workers in your own infrastructure. Workers execute tasks in Docker containers or Kubernetes pods.
Isolation characteristics:
| Aspect | Temporal | Trigger.dev Managed | Trigger.dev Self-Hosted |
|---|---|---|---|
| Worker isolation | Process-level (shared memory possible) | Container-per-task | Configurable (container or process) |
| State storage | Temporal database | Managed PostgreSQL | Your database |
| Secrets management | Environment variables | Encrypted secrets API | Your secrets manager |
| Network boundaries | Workers access external APIs directly | Egress through managed NAT | Your network topology |
| Worker configuration | Explicit pool sizing, task queue assignment | Fully abstracted, auto-scaled | Manual configuration required |
For agent tasks that call third-party APIs (OpenAI, Anthropic, web scrapers), Trigger.dev’s container isolation prevents one task from leaking credentials or state to another. Temporal’s process-level isolation requires careful worker configuration to achieve the same guarantee.
Observability and Debugging
Temporal provides a web UI that shows workflow history, pending activities, and task queue metrics. You can replay workflows locally using the same event history. The deterministic replay model makes debugging easier: given the same inputs and history, you get the same execution.
Trigger.dev provides a dashboard that shows task runs, logs, and execution traces. Each task run has a timeline view showing checkpoints, retries, and external calls. You can inspect serialized state at any checkpoint.
Debugging primitives:
- Temporal: Time-travel debugging through replay. Attach a debugger to a local worker and replay production workflows.
- Trigger.dev: Inspect checkpointed state in the dashboard. Re-run failed tasks with modified inputs.
For agent debugging, Temporal’s replay model helps when you need to reproduce a specific LLM response or tool call sequence. Trigger.dev’s checkpoint inspection helps when you need to understand why a task stalled or what state it held at failure.
Code Example: Multi-Step Agent Task
Here’s how a research agent task looks in Trigger.dev V2:
import { task } from "@trigger.dev/sdk/v3";
import { anthropic } from "@ai-sdk/anthropic";
import { generateText, CoreMessage } from "ai";
import { z } from "zod";
// Tool definitions (simplified for example)
const search = {
description: "Search the web",
parameters: z.object({ query: z.string() }),
execute: async ({ query }: { query: string }) => {
// Call search API
return { results: [] };
},
};
const browse = {
description: "Browse a URL",
parameters: z.object({ url: z.string() }),
execute: async ({ url }: { url: string }) => {
// Fetch and parse URL
return { content: "" };
},
};
const analyze = {
description: "Analyze data",
parameters: z.object({ data: z.string() }),
execute: async ({ data }: { data: string }) => {
// Run analysis
return { insights: [] };
},
};
// Map tool names to implementations
const toolRegistry: Record<string, any> = {
search,
browse,
analyze
};
async function executeTool(toolCall: { toolName: string; args: Record<string, any> }) {
const tool = toolRegistry[toolCall.toolName];
if (!tool) {
throw new Error(`Unknown tool: ${toolCall.toolName}`);
}
return await tool.execute(toolCall.args);
}
export const researchAgent = task({
id: "research-agent",
retry: {
maxAttempts: 3,
factor: 2,
minTimeout: 1000,
},
run: async ({ topic }: { topic: string }) => {
const messages: CoreMessage[] = [
{ role: "user", content: `Research: ${topic}` },
];
// Checkpoint before each LLM call
for (let i = 0; i < 10; i++) {
// Runtime checkpoints here; if task crashes, it resumes with the same messages array
const { text, toolCalls, steps } = await generateText({
model: anthropic("claude-opus-4-20250514"),
system: "You are a research assistant with web access.",
messages,
tools: {
search,
browse,
analyze,
},
maxSteps: 5,
});
// If no tool calls, we're done
if (!toolCalls.length) {
return { summary: text, stepsUsed: steps.length };
}
// Execute tools and checkpoint after each
for (const call of toolCalls) {
const result = await executeTool(call);
messages.push({
role: "tool",
content: JSON.stringify(result)
});
}
}
throw new Error("Max iterations reached");
},
});
The runtime checkpoints after each await. If the task crashes during executeTool, it resumes from the last checkpoint with the same messages array. You don’t write explicit checkpoint calls or manage state serialization.
Temporal architectural comparison:
In Temporal, you’d split this into a workflow (orchestration logic) and activities (LLM calls, tool execution). The workflow would be deterministic, and activities would handle side effects:
| Component | Temporal | Trigger.dev |
|---|---|---|
| Orchestration logic | Workflow function (deterministic, no side effects) | Task function (imperative, side effects allowed) |
| External API calls | Activity functions (separate registration, worker pools) | Direct async/await in task body |
| State management | Event history replay | Checkpoint serialization at await points |
| Code organization | Split across workflow/activity files, explicit registration | Single task function, implicit runtime handling |
| Failure recovery | Replay from event log | Resume from last checkpoint |
The Temporal version requires more boilerplate (activity definitions, worker registration) but provides stronger guarantees about determinism and replay. Trigger.dev collapses the workflow/activity distinction into a single task function, reducing setup time but removing replay guarantees.
When to Use Each
Choose Trigger.dev when:
- You want to write TypeScript background jobs without learning workflow semantics
- Your tasks are mostly linear with occasional branching (agent loops, data pipelines)
- You need fast iteration and don’t want to run infrastructure
- Container-level isolation matters for security or multi-tenancy
- Your team already knows async/await patterns and doesn’t want to learn event sourcing
Choose Temporal when:
- You need long-running workflows (days or weeks) with complex state machines
- You require exactly-once semantics with deterministic replay
- You want to version workflows and migrate running instances
- You need polyglot support (Go, Java, Python workers alongside TypeScript)
- You already run Kubernetes and can operate a Temporal cluster
For most agent tasks (research loops, content generation, API orchestration), Trigger.dev’s simpler model reduces cognitive load. For complex multi-agent systems with compensation logic and long-running coordination, Temporal’s guarantees justify the complexity.
Technical Verdict
Trigger.dev V2 trades Temporal’s deterministic replay for developer ergonomics. You get durable execution without event sourcing, automatic retries without activity boilerplate, and managed infrastructure without running a cluster.
Use Trigger.dev if:
- Your agent pauses at LLM API calls and tool invocations (natural checkpoint boundaries) and you prioritize time-to-first-task over replay guarantees
- You’re building a multi-tenant agent system where each customer’s tasks need container-level isolation for security
- Your failure modes are “retry with exponential backoff or alert a human” rather than “replay the exact sequence for audit compliance”
- You need to ship agent workflows in days (research loops, content pipelines, notification sequences) without infrastructure overhead
- Your tasks complete in minutes to hours, not days or weeks
Avoid Trigger.dev if:
- You need sub-second task invocation latency or fan-out to thousands of parallel tasks. Temporal’s worker pools handle high-throughput scenarios more efficiently than managed container orchestration.
- You require deterministic replay for regulatory compliance (financial transactions, healthcare workflows) or complex debugging scenarios where you must reproduce exact LLM responses
- You need to version long-running workflows (7-day research projects, month-long approval chains) and migrate instances in-flight without downtime
- Your team already operates Temporal infrastructure and knows the workflow/activity pattern (switching costs outweigh ergonomic gains)
- You need polyglot workers (Python for ML, Go for high-throughput data processing) alongside TypeScript orchestration
The core limitation is that you can’t replay arbitrary execution points or time-travel through workflow history. If a task fails in a way that’s hard to reproduce (non-deterministic LLM output, race condition in tool execution), you inspect logs and checkpointed state rather than replaying the exact sequence. For agent builders focused on shipping fast and iterating on prompts and tool chains, this tradeoff is acceptable. For teams building regulated systems or debugging complex multi-agent coordination, Temporal’s replay model justifies the operational complexity.