Scheduling Cron Jobs in Serverless: Patterns, Pitfalls and Real Examples
Cron jobs and serverless seem philosophically opposed. Cron expects a server that's always running; serverless compute spins up on demand and tears down after execution. In practice, modern serverless platforms handle scheduled jobs natively — and there are real advantages over traditional cron.
Why Serverless Cron Is Better Than Traditional Cron
- No idle server costs — your cron function consumes compute only during execution, not the 23 hours and 50 minutes it's waiting.
- Automatic scaling — if your nightly report job suddenly needs to process 10x more data, the platform scales without a capacity change request.
- Built-in retry — when a job fails, the scheduler can retry automatically with configurable backoff.
- Centralised scheduling — all schedules in one UI, with execution history and alerting.
Pattern 1: Simple Periodic Tasks
The most common pattern: a function that runs on a fixed schedule. Clean up expired sessions, archive old logs, send a daily email digest.
// moqapi.dev function — daily DB cleanup
export const handler = async (event) => {
const cutoff = new Date()
cutoff.setDate(cutoff.getDate() - 30)
const deleted = await db.query(
"DELETE FROM sessions WHERE created_at < $1 RETURNING id",
[cutoff]
)
return {
statusCode: 200,
body: { deleted: deleted.rowCount, cutoff: cutoff.toISOString() }
}
}
// Schedule: 0 2 * * * (2 AM every day)
Pattern 2: Fan-Out Jobs
For jobs that process a list of items (send emails to all users, process a queue), split the work: use a lightweight cron invocation to fetch IDs and enqueue them, then let worker functions process each item in parallel.
// Orchestrator — runs every 10 minutes
export const handler = async () => {
const pendingOrders = await db.query(
"SELECT id FROM orders WHERE status = 'pending' LIMIT 100"
)
await Promise.all(
pendingOrders.rows.map(order =>
queue.enqueue({ type: 'process_order', orderId: order.id })
)
)
return { statusCode: 200, body: { enqueued: pendingOrders.rowCount } }
}
Pattern 3: Health Checks and Uptime Monitors
A cron that pings external services every five minutes and posts to Slack if one goes down is a simple and reliable uptime monitor. No third-party monitoring service required.
// Health check — every 5 minutes
export const handler = async () => {
const services = ["https://api.stripe.com", "https://api.sendgrid.com"]
const results = await Promise.all(
services.map(async url => {
try {
const res = await fetch(url, { signal: AbortSignal.timeout(5000) })
return { url, ok: res.ok, status: res.status }
} catch {
return { url, ok: false, status: 0 }
}
})
)
const failures = results.filter(r => !r.ok)
if (failures.length > 0) {
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: "POST",
body: JSON.stringify({ text: "🚨 Services down: " + failures.map(f => f.url).join(", ") })
})
}
return { statusCode: 200, body: results }
}
Pattern 4: Data Sync
Syncing data between a third-party CRM, analytics platform, or warehouse runs well as a scheduled job. The pattern: fetch from source → transform → upsert into destination. Run every hour for near-real-time sync.
Pattern 5: Report Generation
Generate weekly business reports, export them to R2 or S3, and send download links to stakeholders. Serverless is ideal because report generation can spike in memory and request time — scale automatically, pay only for execution time.
Pitfalls to Avoid
- Overlapping runs — if a job takes longer than its schedule interval, multiple instances run concurrently. Guard with a distributed lock (a DB row or Redis key) at job start.
- Silent failures — a function that returns 200 but does nothing is indistinguishable from success. Emit structured logs with quantitative output (records processed, errors encountered) so you can monitor trends.
- Missing timezone handling —
0 9 * * *runs at 9 AM UTC, which is 2 AM Pacific. Be explicit about timezone in your scheduler config.
About the Author
Founder and sole developer of moqapi.dev. Full-stack engineer with deep experience in API platforms, serverless runtimes, and developer tooling. Built moqapi to solve the mock data and deployment friction she experienced firsthand building production APIs.
Related Articles
Building Serverless APIs: 10 Best Practices You Should Follow
From cold-start optimisation to function composition, learn battle-tested patterns for shipping production-grade serverless APIs at scale.
Serverless Function Deployment Without AWS: A Practical Comparison
AWS Lambda isn't the only game in town. This guide compares Cloudflare Workers, Deno Deploy, Vercel Edge, and moqapi.dev for deploying serverless functions in 2026.
Azure Functions Is Powerful — But These Pain Points Are Slowing Your Team Down
Cold starts, complex deployment pipelines, vendor lock-in, and billing surprises: the real problems teams hit with Azure Functions, and how moqapi.dev's serverless runtime removes every one of them.
Ready to build?
Start deploying serverless functions in under a minute.