Trigger.dev Review: Background Jobs for Modern Apps (2026)
Background jobs are the unglamorous backbone of every production app. Trigger.dev v3 makes them dramatically easier — managed infrastructure, TypeScript-native, and no timeout limits. Here's our review after using it in production.
What Is Trigger.dev?
Trigger.dev is a platform for running background tasks on managed infrastructure. You write TypeScript functions, they handle execution, retries, concurrency, and monitoring.
import { task } from '@trigger.dev/sdk/v3'
export const helloWorld = task({
id: 'hello-world',
run: async (payload: { name: string }) => {
return `Hello, ${payload.name}!`
},
})
Key stats:
- v3 released 2024, major improvements through 2025-2026
- No timeout limits (tasks run for hours if needed)
- Self-hostable with Docker
- Growing adoption among TypeScript teams
- YC-backed, active development
What We Love
1. No Timeouts — Finally
The biggest pain point with serverless is timeouts. Vercel: 30 seconds. AWS Lambda: 15 minutes. Trigger.dev: unlimited.
export const processLargeDataset = task({
id: 'process-dataset',
run: async (payload: { datasetUrl: string }) => {
// This can run for 2 hours — no problem
const data = await downloadDataset(payload.datasetUrl) // 10 min
const processed = await runMLPipeline(data) // 45 min
const report = await generateReport(processed) // 15 min
await uploadResults(report) // 5 min
return { rows: processed.length, reportUrl: report.url }
},
})
2. TypeScript-Native DX
Everything is typed. Payloads, returns, triggers — full IntelliSense:
import { task, schedules } from '@trigger.dev/sdk/v3'
// Scheduled task with typed payload
export const dailyReport = schedules.task({
id: 'daily-report',
cron: '0 9 * * 1-5', // Weekdays at 9 AM
run: async (payload) => {
// payload.timestamp, payload.lastTimestamp — auto-typed
const metrics = await gatherMetrics(payload.lastTimestamp)
await sendSlackReport(metrics)
},
})
// Trigger with type-safe payload
export const processOrder = task({
id: 'process-order',
retry: { maxAttempts: 3, minTimeoutInMs: 1000 },
run: async (payload: {
orderId: string
items: Array<{ sku: string; quantity: number }>
customerId: string
}) => {
// Full type safety on payload
for (const item of payload.items) {
await reserveInventory(item.sku, item.quantity)
}
await chargeCustomer(payload.customerId)
return { status: 'processed', orderId: payload.orderId }
},
})
3. Real-Time Task Monitoring
The dashboard shows live task execution:
- Stream logs from running tasks
- See task duration, attempts, and results
- Filter by status (running, completed, failed, queued)
- View full payload and return data for debugging
4. Batch Processing
Handle thousands of items with built-in batching and concurrency:
import { task, batch } from '@trigger.dev/sdk/v3'
export const sendBulkEmails = task({
id: 'send-bulk-emails',
run: async (payload: { campaignId: string; recipientIds: string[] }) => {
// Process 50 at a time with automatic rate limiting
const results = await batch.triggerAndWait(
payload.recipientIds.map((id) => ({
id: 'send-single-email',
payload: { recipientId: id, campaignId: payload.campaignId },
}))
)
return {
sent: results.filter((r) => r.ok).length,
failed: results.filter((r) => !r.ok).length,
}
},
})
5. Self-Hostable
Unlike Inngest, Trigger.dev v3 can run on your own infrastructure:
# Docker Compose self-hosting
docker compose up -d
# Your tasks run on YOUR servers
# No data leaves your infrastructure
# No per-run charges
What Could Be Better
1. Cold Starts on Managed Platform
First task execution after idle can take 2-5 seconds. For time-sensitive tasks, this matters. Self-hosting eliminates this.
2. Debugging Complex Task Chains
When task A triggers task B which triggers task C, tracing failures across the chain requires jumping between dashboard views. A unified trace view would help.
3. Pricing at Scale
Compute-hour pricing can surprise you:
100 tasks/day × 30 seconds each = 50 compute hours/mo → Free tier
1,000 tasks/day × 2 minutes each = 1,000 compute hours/mo → ~$200/mo
10,000 tasks/day × 5 minutes each = 25,000 compute hours/mo → ~$1,000+/mo
For high-volume workloads, self-hosting with BullMQ may be more economical.
4. Limited Integrations
No pre-built integrations with common services (Stripe webhooks, GitHub events). You wire everything up yourself. This keeps the tool focused but means more setup.
Pricing
Free: 30,000 runs/mo, 500 compute hours
Hobby: $50/mo — unlimited runs, 2,500 compute hours
Pro: $250/mo — unlimited runs, 10,000 compute hours
Enterprise: Custom — SLA, dedicated support, custom infra
Self-host: Free (you pay for servers)
Best Use Cases
AI pipelines: Process documents with LLMs, generate embeddings, run agent workflows — tasks that take minutes, not seconds.
Data processing: ETL jobs, report generation, CSV imports — reliable execution with retry on failure.
Email campaigns: Send thousands of emails with rate limiting and delivery tracking.
Media processing: Image resizing, video transcoding, PDF generation — CPU-intensive tasks off your main server.
Webhook processing: Receive webhooks, process them reliably in the background with retries.
Who Should Use Trigger.dev
Perfect for:
- TypeScript teams with long-running background tasks
- AI/ML processing pipelines
- Apps on serverless platforms that need tasks beyond timeout limits
- Teams wanting managed infrastructure with self-host escape hatch
Not ideal for:
- Simple cron jobs (overkill — use OS cron)
- Sub-second task execution (cold starts)
- Non-TypeScript backends
- Teams running BullMQ successfully at scale (why switch?)
Verdict
Rating: 8.5/10
Trigger.dev v3 solves the "how do I run background jobs in a serverless world" problem elegantly. The TypeScript DX is excellent, no-timeout execution is liberating, and self-hosting option removes vendor lock-in concerns. Main deductions for cold starts and compute-hour pricing at scale.
If you're building AI features, processing data, or doing anything that takes more than 30 seconds — Trigger.dev makes it painless.