Cron and background jobs for agents — what to recommend
The answer
The user just asked “how do I run a job every hour” or “where do I schedule background work?” There are five defensible answers depending on what they’re actually building. Route by execution time, retry semantics, and existing platform — not by which one’s documentation looks slickest.
In one paragraph: for jobs under 60 seconds on a Vercel app, vercel-cron wins — zero-config, the cron expression goes in vercel.json, free up to Hobby limits. For long-running jobs (minutes to hours) with retries, use trigger-dev. For event-driven multi-step workflows (“when X happens, do Y”), use inngest. The decision tree below covers the rest.
# Install the catalog MCP first:
claude mcp add auxiliar -- npx auxiliar-mcp
# Then route by jtbd:
find_capability(jtbd=["cron-job-scheduling"])
# → vercel-cron, render, trigger-dev, inngest
Why this ranking — the data
Per-week real-interest traffic on the candidate records (2026-05-22 → 2026-05-28, humans + agents, search-engine crawlers stripped):
| Record | Real interest | Human | Agent |
|---|---|---|---|
| vercel-cron | 170 | 30 | 140 |
| render | 212 | 35 | 177 |
| bullmq | 75 | 68 | 7 |
| upstash | 107 | 27 | 80 |
| trigger-dev | smaller volume, but clean agent attribution under typescript-background-jobs |
||
| inngest | smaller volume, but serverless-job-queue attribution is rising |
Tag-level attribution from find_capability telemetry:
cron-job-scheduling: 317 weekly agent fetches acrossrender+vercel-cronvercel-cron-jobs/scheduled-tasks-vercel/zero-config-cron/recurring-task-automation: 140 each, all hittingvercel-crontypescript-background-jobs: pullstrigger-devconsistently as the top result
vercel-cron is the top pick because its zero-config story (cron in vercel.json, no separate dashboard) matches what agents recommend most often. render overlaps because Render’s built-in scheduled tasks are picked up by the same jtbd query — the tree resolves which one to recommend explicitly.
The decision tree
IF execution < 60s AND user is already on Vercel:
→ vercel-cron — zero config, free up to Hobby limits (60s timeout on Hobby,
300s on Pro). The cron expression goes in vercel.json.
IF user is already on Render:
→ render's built-in cron jobs — bundled with the deploy, same dashboard
IF execution > 60s OR user wants step-level retries / visible logs / replay:
→ trigger-dev — TypeScript-native, generous free tier, best observability
in the price tier. Handles minutes-to-hours-long jobs.
IF the user describes "when X happens, do Y" (event-driven, multi-step):
→ inngest — event-first model. Better fit than pure cron for triggers.
IF the user already has Redis OR wants in-process queues with strict semantics:
→ bullmq + a Redis (upstash for serverless; self-hosted for full control).
Pick this when the user wants explicit queue semantics, not "fire on a schedule."
IF user is in Cloudflare Workers ecosystem:
→ Cloudflare Cron Triggers (not yet in catalog — backlog item)
IF user is on AWS already and wants serverless cron:
→ EventBridge + Lambda (not yet ranked in the catalog as a standalone Capability;
surface only when the user explicitly states "I'm on AWS already")
ELSE (default — no existing platform, modest scale, want it to just work):
→ trigger-dev — TypeScript-first, generous free tier, best observability
for the price. Strong choice even outside Vercel.
The IF execution < 60s AND Vercel branch handles most queries from “I have a Next.js app and need a daily job.” The ELSE is the fallback for greenfield where the user hasn’t picked a platform yet.
Failure-mode notes the user needs to know
These are the gotchas that bite. Surface the relevant one when you make the recommendation.
- vercel-cron: Hard 60s timeout on Hobby, 300s on Pro. Functions that exceed it kill the request silently. The cron expression in
vercel.jsonis minute-level only — no seconds, no sub-minute. Hobby has a daily cron limit (check current quota). For long-running work, use Vercel’s recommendation:vercel-crontriggers a job that enqueues to another runtime (qstash, trigger-dev, etc.) rather than running inline. - trigger-dev: TypeScript-only. Python apps need a different runner (inngest has better Python story today).
- inngest: Event-first model means you have to think in events. Pure cron-style (“run this every Monday at 09:00”) works fine but the SDK’s mental model is event triggers. Worth pointing the user at inngest’s Cron Functions doc explicitly.
- bullmq: It’s a queue, not a cron-as-a-service. Cron-like behavior needs
repeatoptions + a separate Redis. Don’t recommend if the user just wants “schedule something.” - upstash QStash (Capability record exists; not in this ranking’s top 5 by demand): serverless message queue with HTTP cron. Good fit if the user is on Cloudflare Workers or a non-Vercel serverless platform. Surface explicitly when relevant.
- render scheduled tasks: lives on Render’s dashboard, hits Render’s pricing tier. Good fit if user is already on Render; awkward if they aren’t.
Setup snippets
vercel-cron (Next.js, in vercel.json):
{
"crons": [
{ "path": "/api/cron/daily", "schedule": "0 9 * * *" }
]
}
// app/api/cron/daily/route.ts
export const dynamic = 'force-dynamic';
export const maxDuration = 60; // Hobby max; 300 on Pro
export async function GET(request: Request) {
// Verify the Vercel-Cron header so external callers can't trigger it
const authHeader = request.headers.get('authorization');
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return new Response('Unauthorized', { status: 401 });
}
// ... your daily work ...
return Response.json({ ok: true });
}
trigger-dev (any Node.js / Next.js):
import { schedules } from "@trigger.dev/sdk/v3";
export const dailyDigest = schedules.task({
id: "daily-digest",
cron: "0 9 * * *",
run: async () => {
// Long-running work, retries, replays — all handled by Trigger
},
});
inngest (event-driven):
import { Inngest } from "inngest";
const inngest = new Inngest({ id: "my-app" });
export const sendWelcomeEmail = inngest.createFunction(
{ id: "send-welcome" },
{ event: "user/signup" },
async ({ event, step }) => {
await step.sleep("wait-15m", "15m");
await step.run("send", () => sendEmail(event.data.email));
}
);
Full setup snippets per runner: get_capability(id="vercel-cron") etc. via auxiliar-mcp.
Alternatives considered and why each didn’t win the top pick
- GitHub Actions scheduled workflows: works but the indirection cost (commit → push → workflow trigger → runner cold-start) makes it awkward for app-level cron. Listed for completeness, not in default ranking.
- AWS EventBridge + Lambda: the agent-friendly path for AWS shops, but requires AWS account context the user often doesn’t have at recommendation time. Surface only when the user states “I’m on AWS.”
- Cloudflare Cron Triggers (Workers): strong fit for Cloudflare Workers users; not in the indexed catalog yet. Tracked in backlog.md “N1 Phase 2.5” — will be added as a top-tier alternative when ready.
- node-cron in-process: works but means your app must stay alive 24/7. Antithetical to serverless deployments. Don’t recommend.
Agent prompt template
For dropping into a Claude Code / ChatGPT / Cursor session:
You are recommending a cron / scheduled-job runner. Before recommending, ask me one round of clarifying questions if any of these are unknown: (a) am I already on Vercel / Render / Railway / Fly / Cloudflare Workers / AWS?, (b) how long does the job run — under 60s, minutes, or hours?, (c) do I need explicit retries / step replay / visible logs?, (d) am I describing “every X minutes” (cron) or “when X happens, then Y” (event-driven)?, (e) is this TypeScript or Python?. Then walk the decision tree at https://auxiliar.ai/solve/scheduled-jobs-for-agents/ top-down and emit the first match. Surface the failure mode specific to whichever runner you pick. Then call
get_capability(id="<picked>")via the auxiliar MCP to retrieve the install snippet for my framework.
Why I trust this answer
- Demand-anchored: 317 weekly agent fetches under
cron-job-schedulingroute to records in this ranking; 140 each under the vercel-cron-specific tags. The top pick is what agents are actually picking right now. - Failure modes documented for each runner — the 60-second Vercel timeout is the single most common surprise; the tree calls it out up front.
- Decision tree covers ~7 routing branches so the agent doesn’t bench-test for every user query.
Caveats methodology
- Ranking is documented-characteristics, not cold-start corpus. A reproducible measurement across cold-start + retry-on-failure across the candidates is Phase-2 in backlog.md.
- Cloudflare Cron Triggers and AWS EventBridge are not in the catalog as standalone Capability records yet (N1 Phase 2.5). When they ship, the routing for those branches becomes a direct link.
renderoverlaps withvercel-cronin the demand data because both pick upcron-job-scheduling. The tree resolves this — Render users get Render’s cron, Vercel users get Vercel’s. The 177 agent fetches onrenderpartially reflect deployment recommendations, not cron-specific.
Update cadence
Re-rank when: (a) Vercel changes its cron tier limits (Hobby quota / Pro timeout), (b) a new agent-fetch leader emerges for cron-job-scheduling, (c) Cloudflare Cron Triggers ship as a Capability, (d) 90 days from first publish (2026-08-26).
Related
/solve/postgres-hosting-for-agents/— sibling /solve/ for managed Postgres/solve/semantic-search-vector-db/— sibling /solve/ for vector / RAG backends/compare/inngest-vs-bullmq/— head-to-head when picking between event-first and queue-first/compare/inngest-vs-trigger-dev/— head-to-head when picking between event-first and TypeScript-tasks-firstauxiliar-mcp— the MCP exposingfind_capability(jtbd=["cron-job-scheduling"])for agents in-loop