I spent a week evaluating both of these for long-running agent work, and most of the comparison collapses into one question. Where does your code actually execute?
Inngest holds the state machine and calls functions that run on infrastructure you own. Trigger.dev runs your code on its own machines, one container per run, billed by the second. Everything downstream of that, the way you write the code, what you pay, what breaks under load, follows from that split.
Both are good. I want to say that up front, because comparison posts have a habit of picking a winner in the first paragraph and then reverse-engineering reasons. These solve the same problem well and they'd suit different teams.
The code shape follows the architecture
In Inngest you wrap units of work in step.run(), and each step's result gets memoized, so a failure resumes from the last completed step instead of the top. In Trigger.dev you write a plain async function and the platform snapshots the process, using CRIU to capture memory, registers, and open file descriptors.
Here's the same job on each.
import { task } from "@trigger.dev/sdk";
import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
export const summarizeDoc = task({
id: "summarize-doc",
retry: { maxAttempts: 3 },
run: async (payload: { documentUrl: string }) => {
const doc = await fetch(payload.documentUrl).then((r) => r.text());
const { text } = await generateText({
model: anthropic("claude-sonnet-5"),
prompt: `Summarize this document:\n\n${doc}`,
});
return { summary: text };
},
});import { inngest } from "./client";
import { generateText } from "ai";
import { anthropic } from "@ai-sdk/anthropic";
export const summarizeDoc = inngest.createFunction(
{
id: "summarize-doc",
triggers: { event: "doc/summarize.requested" },
retries: 3,
},
async ({ event, step }) => {
const doc = await step.run("fetch-document", async () => {
const res = await fetch(event.data.documentUrl);
return res.text();
});
const summary = await step.run("summarize", async () => {
const { text } = await generateText({
model: anthropic("claude-sonnet-5"),
prompt: `Summarize this document:\n\n${doc}`,
});
return text;
});
return { summary };
}
);The Inngest version is event-triggered because that's the model. When several consumers should react to one published event, that's genuinely the better shape and Trigger.dev has no direct equivalent.
The determinism thing I got wrong at first
I'd written in my notes that Inngest imposes no determinism constraints, that Date.now() and Math.random() are safe anywhere. That's not right, and their guide on working with loops says so.
Place non-deterministic logic (like API calls, database queries, or random number generation) inside
step.runcalls.
The reason sits on the same page. Each step executes as a separate HTTP request, so the handler re-enters from the beginning every time. A bare Math.random() outside a step produces a different value on each re-entry.
What's true is that Inngest is much softer about this than deterministic replay. Reorder your steps and you get warnings where a replay engine would have failed the run. Their versioning docs call it "graceful determinism by default," and having migrated off a replay-based system before, that difference is real and it matters. The constraint doesn't disappear though. It relocates into one rule, durable work goes inside a step.
Trigger.dev sidesteps this because it checkpoints the process rather than replaying your function. You don't get the per-step event history that Inngest's model gives you for free, which is a genuine loss when you're debugging.
Pricing is where the comparisons fall apart
I read several third-party comparisons before writing this and kept finding wrong numbers. Two listed Inngest at $75/mo. One had Trigger.dev's Hobby tier at $20. Inngest's own pricing page has a line that's easy to misread, and I misread it. The worker overage says $10 per 10 workers, not $10 per worker.
So, current figures, pulled from both pricing pages this week.
| Trigger.dev | Inngest | |
|---|---|---|
| Free | $5 monthly credits, 20 concurrent runs | 50k executions, 5 concurrent steps |
| Entry paid | $10/mo (Hobby) | From $99/mo (Pro, 1M executions included) |
| Mid | $50/mo (Pro) | |
| Billing unit | Compute-seconds plus $0.25 per 10,000 runs | Executions, meaning one run plus each step, alongside separate meters for events, span data, and eval scores |
| Concurrency | $10/mo per 50 above 200 runs | $25/mo per 25 above 100 steps |
| Trace retention | 30 days on Pro | 7 days on Pro, 90 on Enterprise |
Two things get lost in tables like that one.
Inngest's unit is an execution, and steps count toward it. Their FAQ puts it plainly, five step.run() calls means one run consumes six executions, the run itself plus five steps. So the decomposition that buys you finer failure recovery also multiplies the bill. Trigger.dev charges per run invocation plus actual compute, and however you structure the inside of a task is free.
The bigger one is that Inngest's price covers orchestration and nothing else. Your functions run on your Vercel or AWS or Fly account and that invoice shows up separately. $99 next to $50 is only a comparison once you add your own hosting bill to the Inngest side.
There's a nuance in Trigger.dev's favor that took me a while to find, and it has two thresholds rather than one. When a task waits, compute billing stops after five seconds. The concurrency slot releases separately, sixty seconds into a wait.for or wait.until, once the machine is snapshotted and shut down. Waiting on a subtask with triggerAndWait checkpoints immediately. I'd assumed one number covered both and put it into a cost estimate that came out ten times low.
One naming collision cuts against the framing I started with. Trigger.dev's Hobby is the $10 paid tier and Inngest's Hobby is the free one.
Timeouts, which is why I looked at this at all
I run agent loops that take minutes, so this was the deciding axis for me.
Trigger.dev imposes no ceiling of its own. You set maxDuration in trigger.config.ts and choose how high it goes. Machine presets run from Micro at a quarter vCPU up to Large 2x at 8 vCPU and 16 GB.
Inngest depends on how you deploy. Functions served from your own serverless endpoints inherit that host's timeout, and their Vercel docs are direct about it, recommending you set maxDuration on the endpoint and keep checkpointing's maxRuntime twenty to forty percent below it. Two documented ways around that. Streaming lets a function run past maxDuration, and Connect, their long-lived worker mode, isn't bound by HTTP timeouts at all. Connect needs a long-running server though, so Lambda and Vercel are out for that path.
Neither pauses forever for a human. Trigger.dev's createToken takes a timeout defaulting to ten minutes, with seven days as the longest example in their docs.
Licensing, briefly, because I see it overstated
Trigger.dev's platform is Apache 2.0 and its client SDKs are MIT. Both OSI-approved.
Inngest splits. Every SDK you import, TypeScript, Python, Go, is Apache 2.0, so nothing copyleft enters your dependency tree. The engine is under the SSPL, which MongoDB withdrew from OSI review in 2019 and which the OSI has since said is not an open source license. Inngest pairs it with a future license that converts each release to Apache 2.0 on its third anniversary.
For the normal case, running your own functions against Inngest Cloud or self-hosting the engine for yourself, none of this binds you. Section 13 attaches if you resell workflow execution as a service. In practice the thing most likely to cost anyone anything is a procurement policy that only accepts OSI-approved licenses, which has nothing to do with the license's actual obligations.
Where each one wins
Inngest, if your architecture is event-driven and you want fan-out and batching to fall out of the model instead of being wired up by hand. Its dev server ships as a Docker image, auto-discovers your app, and takes a dummy event key. Offline local development actually works. Trigger.dev's dev loop wants internet. Trigger.dev runs Python scripts from inside TypeScript tasks through a build extension, which is not the same as the native Python and Go SDKs Inngest ships. If your functions are short and numerous on servers you already pay for, a million included executions goes a long way. And Agent Evals makes evaluation scores a billed primitive, which Trigger.dev has no equivalent for today.
Trigger.dev, if the work runs long or wants real memory, if you'd rather not operate the compute, or if you want a task to hold a container with real CPU and RAM without competing with the process serving your users.
I landed on Trigger.dev, for agent loops specifically. If my jobs were fifty thousand short webhook handlers a day I'd have gone the other way without much agonizing.
Frequently asked
- Which one is cheaper?
- Depends on the shape of the work, and any answer that skips that is selling something. Short, high-volume, few-step functions on servers you already run favor Inngest, and its $99 tier includes a million executions. Long-running or memory-hungry work favors Trigger.dev, because provisioning your own boxes for spiky heavy jobs is exactly the cost the Inngest number leaves out.
- Can I run a multi-minute AI agent on either?
- On Trigger.dev, yes, with no platform ceiling, though you set a maxDuration yourself in trigger.config.ts. On Inngest it depends on how you deploy. Functions served from your own serverless endpoints inherit that host's timeout. Streaming and Connect workers both get around it.
- Does either let me pause forever waiting on a human?
- Not indefinitely. Trigger.dev waitpoints take a timeout that defaults to ten minutes, and the longest example in their docs is seven days. Inngest's step.waitForEvent takes a timeout too.
- Is Inngest open source?
- The SDKs you actually import are Apache 2.0, so nothing copyleft lands in your dependency tree. The engine is under the SSPL, which the OSI has said is not an open source license, and each release converts to Apache 2.0 on its third anniversary.
