If you have ever written setTimeout(() => doExpensiveWork(), 0) in a Next.js API route, you made a bet. The request finishes, the connection closes, and the work either completes or disappears with no record of which one happened. The first action is to list your background jobs with three facts each: what triggers it, how long it can run, and what a silent failure costs you.
Every B2B SaaS ends up with this list: invoice generation, webhook retries, nightly aggregations, email sequences, PDF exports and AI enrichments that take too long for a user-facing request. The question is which layer owns them. Three credible paths exist for a serverless product: Inngest, Trigger.dev, or your own scheduler and queue. This post compares them using the vendors' own documentation, checked on 30 September 2026. Prices and limits change, so re-read the linked pages before you commit.
For the wider picture of jobs, webhooks, email and admin tooling, the SaaS backend infrastructure guide is the parent article, and Inngest for SaaS background jobs goes deeper on one option.
Is Vercel Cron enough for background jobs in a SaaS?
Vercel Cron is a scheduler, not a job system: it calls one of your routes on a schedule, does not retry an invocation that fails, and can occasionally deliver the same run twice.
Vercel's documentation says cron delivery is best effort: a transient network error can prevent a request from reaching your function, in which case no runtime log exists for that run. It recommends designing jobs to be idempotent and reconciliation-based, so each run reprocesses everything outstanding since the last success, and using a lock when runs could overlap. Cron expressions are always in UTC.
The plan limits matter for the choice. According to Vercel's cron usage and pricing page, Hobby allows cron jobs once per day with per-hour precision (a 0 1 * * * job can fire anywhere between 1:00 and 1:59), while Pro allows once per minute with per-minute precision. Every plan allows 100 cron jobs per project. Cron jobs run as Vercel Functions, so function duration limits apply: a default of 300 seconds on every plan, a maximum of 800 seconds on Pro and Enterprise, and a 30-minute extended maximum in beta. If you remember a 60-second Pro cap from older guides, it no longer matches the current documentation.
A cron route is fine for a daily digest over a small dataset. It is the wrong tool when you need retries, per-job failure alerts, replay or work that fans out per customer. Vercel itself points to its Workflow and Queues products for durable work, which is a third option to evaluate if you already deploy there.
Inngest vs Trigger.dev: which fits your SaaS job pattern?
Inngest fits event-triggered, multi-step workflows because it memoizes each completed step, while Trigger.dev fits long-running tasks because its runs have no timeout and re-execute the whole run function when they retry.
How does Inngest handle retries and cost?
Picture a five-step job that fails on step 4. Inngest's execution docs explain that the function is re-executed with the stored results of earlier steps injected, so completed steps are not run again and only the failed step repeats. The idempotency burden shrinks to the step that failed: an insert step should be an upsert, and an email step needs a sent guard.
Billing follows the same model. Inngest's pricing page defines an execution as a function run or a step execution, and states that a function with 5 step.run() calls uses 6 executions. Consider a hypothetical product running 10,000 jobs a month with 4 steps each: that is 10,000 x (1 + 4) = 50,000 executions, exactly the Free plan quota, and Free pauses execution when the quota is hit. At 100,000 such jobs you would use 500,000 executions, inside the 1M included with Pro. The exact overage rates are tiered, from $0.000050 down to $0.000015 per execution, so model your own step counts rather than trusting the headline price.
When does Trigger.dev win?
Trigger.dev's pricing FAQ says tasks can run for as long as you need, with no timeouts, on every plan. That removes the need to chop a video transcode or a long crawl into timeout-safe pieces. The cost is on the retry side: its retry docs say a failed attempt runs the whole run function again, and suggest splitting work into smaller tasks that retry independently. Every database write in such a task therefore needs to be an upsert, and every external call needs an idempotency key.
Which one offers the control you need?
Both projects describe themselves as self-hostable, so a self-hosting requirement does not rule either out by itself. Inngest's self-hosting docs describe a single binary that defaults to SQLite and an in-memory Redis with snapshots, which suits single-node use; multi-node needs external PostgreSQL and Redis, and support is not guaranteed for self-hosted instances. Trigger.dev says it is open source and has a self-hosting guide. Read both before you answer a buyer's security questionnaire, and see the SaaS security and compliance guide for the compliance side of that conversation.
| Free | Paid tiers (verified 30 Sep 2026) | History or log retention | |
|---|---|---|---|
| Inngest | $0, 50k executions, 5 concurrent steps | Pro from $99/mo, 1M executions, 100+ concurrent steps; Business from $499/mo, 10M executions, 500 | 24 hours Free, 7 days Pro, 14 days Business |
| Trigger.dev | $0, $5 credits, 20 concurrent runs | Hobby $10/mo, 50 runs; Pro $50/mo, 200+ runs | 1 day Free, 7 days Hobby, 30 days Pro |
Observability follows those numbers. Retention is the practical difference: on the Free tiers you can only look back about a day, so a failure that happened over a weekend may already be gone by Monday. If your on-call person needs to replay last week's failure, price the retention you need as well as the execution quota.
When is a custom queue worth it?
A custom queue is worth it when you already run persistent infrastructure and need behaviour that a hosted platform prices or limits awkwardly, because you then own retries, monitoring and recovery yourself.
What does BullMQ on Redis require?
BullMQ is an MIT-licensed Node.js library that builds a queue on Redis. Its documentation lists retries, repeatable cron-style jobs, per-worker concurrency and recovery from process crashes, and has a section on stalled jobs. It has no per-run fee, but you take on the operations work:
- A persistent Redis instance, not an ephemeral one.
- Worker processes with tuned concurrency and graceful shutdown.
- Stalled-job handling, so a crashed worker does not strand a job.
- A dashboard and alerting you choose or build.
- A policy for jobs that fail repeatedly.
On a purely serverless host you also need a long-lived worker somewhere else, which is another service to deploy and monitor. I am deliberately not giving a job-count break-even here: it depends on your step counts, your Redis cost and your time, and I have no primary source for a universal number. For the Redis side, see Redis, Upstash and Dragonfly for SaaS caching.
What does a custom setup look like in a real project?
Callidus, the React + Firebase + Stripe Connect clinic SaaS I built solo, runs its subscription lifecycle on scheduled Cloud Functions. Per its case study, a coordinator function runs daily at 09:00 UTC and fans out one Cloud Task per trialing tenant, following a 3-day email, 1-day email, expiry with 3-day grace, then suspension sequence, and a second scheduled function at 09:30 UTC escalates tenants with failed payments. The case study lists 15 cron jobs in total. That is a custom scheduler-plus-queue design on the Firebase stack, and it shows the pattern: a scheduler finds the work, a queue fans it out, and each task is small enough to retry alone.
How do you pick without overthinking it?
Pick the lowest-overhead option that covers today's volume, and choose the one whose failure mode you can live with.
- Vercel Cron for a handful of idempotent scheduled jobs where a missed or duplicate run is tolerable.
- Inngest when jobs are event-triggered with several steps and you want step-level retries.
- Trigger.dev when tasks run long, need heavy runtimes, or you want an open-source platform.
- BullMQ or cloud-native queues when you already operate the infrastructure and want flat, predictable costs.
The best SaaS MVP stack defers infrastructure cost until it is a real constraint, and background jobs are a classic case. If you use Supabase Edge Functions as a cron trigger layer, you get function logs and little else, so plan your own alerting.
Which of your jobs would hurt most if it silently failed tonight? Start with that one, and write down its retry and idempotency rules before choosing a tool.



