Serverless functions have a dirty secret: they time out. Your 15-minute video transcode, your bulk email send, your nightly data crunch — they all hit a wall the platform imposes. Trigger.dev (https://trigger.dev) is an open-source framework built specifically for long-running, durable background tasks with retries, concurrency control, and no artificial timeout. It has a cloud offering and can be self-hosted. I run PandaStack; here's how the pieces fit and where PandaStack does the hosting.
What Trigger.dev actually is
Trigger.dev lets you write background tasks as plain TypeScript, then trigger them from your app. The framework handles queuing, retries, and long execution. There are two deployment shapes:
- Trigger.dev Cloud — you write tasks, they run them. Simplest.
- Self-hosted — you run the Trigger.dev components yourself and your tasks run on your infrastructure.
Either way, your app (the thing that triggers tasks and shows results to users) is a normal web app you deploy on PandaStack. If you self-host, the Trigger.dev services and task workers also run as containers on PandaStack.
Step 1: Define a task
// src/trigger/transcode.ts
import { task } from "@trigger.dev/sdk/v3"
export const transcodeVideo = task({
id: "transcode-video",
retry: { maxAttempts: 3 },
run: async (payload: { url: string }) => {
// This can run for many minutes — no timeout wall.
const result = await doHeavyTranscode(payload.url)
return { output: result }
},
})No timeout means you write the honest version of the task, not a chopped-up state machine hacked around a 30-second limit.
Step 2: Trigger it from your app
import { tasks } from "@trigger.dev/sdk/v3"
import type { transcodeVideo } from "./trigger/transcode"
// e.g. inside an API route
await tasks.trigger<typeof transcodeVideo>("transcode-video", {
url: "https://uploads.example.com/raw.mov",
})Your API returns instantly; the task runs in the background and you poll or subscribe for the result.
Step 3: The app container
Your triggering app is standard. Dockerfile:
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
EXPOSE 8080
CMD ["node", "dist/server.js"]Step 4: Deploy on PandaStack
If you use Trigger.dev Cloud: you only deploy your app. Push to Git → https://dashboard.pandastack.io → New App → connect repo. Add your Trigger.dev secret key and API URL as encrypted environment variables. Done — tasks run in their cloud, your app runs here.
If you self-host Trigger.dev: you additionally deploy the Trigger.dev services as container apps and back them with managed PostgreSQL (and, depending on version, Redis) from PandaStack's Databases section. Consult the self-hosting guide at https://trigger.dev/docs for the exact services and env vars for your version. Because PandaStack offers Postgres, Redis, containers, and cronjobs in one place, every dependency Trigger.dev needs is available without a second vendor.
CLI for your app:
npm install -g @pandastack/cli
panda login
panda deployTrigger.dev vs a plain PandaStack cronjob
Be honest about what you need:
| Need | Reach for |
|---|---|
| One simple scheduled script | PandaStack cronjob (a container on a schedule) |
| Long-running task triggered by app events, with retries | Trigger.dev |
| Fan-out, concurrency limits, durable multi-step jobs | Trigger.dev |
| Nightly cleanup / digest email | PandaStack cronjob |
Don't run an entire background-jobs framework to delete stale rows once a day. Do run it when your jobs are long, event-driven, and can't fail silently.
Honest tradeoffs
- Self-hosting adds operational surface — several services plus Postgres/Redis to keep updated. Their cloud removes that; self-host for data locality or cost.
- The self-hosted path is newer and more involved than the cloud path — read the current docs, pin versions.
- PandaStack's own cronjobs cover the simple 80% of scheduled work with zero extra infrastructure.
Wrap-up
Trigger.dev is the right answer when background work is long and durable and event-driven. Run your triggering app on PandaStack; if you self-host Trigger.dev, run its services and Postgres/Redis here too. For simple schedules, PandaStack cronjobs already have you covered.
Docs: https://docs.pandastack.io. Start free: https://dashboard.pandastack.io.