Serverless Scheduling: Hands-on with Vercel Cron Jobs
In this era of AI and modern web development, most of us use frameworks like Next.js to build full-stack websites and deploy them on the Vercel platform.
Previously, if you deployed Next.js to Vercel and wanted to perform tasks like "generating database statistics every midnight," you had to register with cron-job.org as we taught in Chapter 2 to ping your API. But Vercel has listened to developers' needs and now natively offers the Vercel Cron Jobs feature!
The benefits of using Vercel Cron Jobs are: deep integration with projects, no need for external services, and built-in robust security protections.
Three Steps to Implement Vercel Cron Jobs
Implementing Vercel Cron in Next.js (App Router) is straightforward. We just need to prepare a regular API endpoint and tell Vercel when to call it in the configuration file.
Step 1: Create an API Route
First, create a dedicated API endpoint for Cron Jobs in your Next.js project.
For example, create the file src/app/api/cron/daily-report/route.ts:
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
// Security check (explained in later steps)
const authHeader = request.headers.get('authorization');
// For safety, log the time of invocation
console.log(`[Cron Job Triggered] Time: ${new Date().toISOString()}`);
try {
// ============================================
// Write your business logic here!
// For example: Fetch yesterday's orders from Supabase and send an email report
// await generateAndSendDailyReport();
// ============================================
return NextResponse.json(
{ success: true, message: 'Daily report processed successfully' },
{ status: 200 }
);
} catch (error) {
console.error('Cron job error:', error);
return NextResponse.json(
{ success: false, message: 'Failed to process job' },
{ status: 500 }
);
}
}
Step 2: Add the Vercel.json Configuration File
Next, create a file named vercel.json in your project's root directory (same level as package.json).
This file is Vercel's dedicated configuration file, where we declare our cron schedules:
{
"crons": [
{
"path": "/api/cron/daily-report",
"schedule": "0 8 * * *"
}
]
}
The configuration is intuitive:
path: Tells Vercel which relative API path to call when the time comes.schedule: Uses Crontab syntax. Here, it's set to run every day at 8 AM (the timezone defaults to UTC, so remember to convert! 8 AM in Taiwan = UTC0 0 * * *).
[!TIP] Vercel Hobby (Free Tier) Limitations If you're on the free tier, Vercel limits Cron Jobs to once per day (i.e., a daily quota of 1 execution). If your script needs to run hourly, you must upgrade to Vercel Pro ($20/month) or fall back to using
cron-job.orgas we previously taught.
Step 3: Security Protection (CRON_SECRET)
This is the most thoughtful feature of Vercel Cron Jobs!
If your API URL (e.g., /api/cron/daily-report) is exposed online, anyone could manually send requests to trigger your report generation, which is highly dangerous.
To solve this, when Vercel reads your vercel.json and sets up the Cron Job, it automatically injects a highly complex password into your project's environment variables: CRON_SECRET.
Every time Vercel triggers your API automatically, its HTTP Header will include this key:
Authorization: Bearer <your_CRON_SECRET>
Thus, we must modify our Step 1 API to add this ironclad protection:
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
// 1. Get the Authorization Header from the request
const authHeader = request.headers.get('authorization');
// 2. Ensure CRON_SECRET is set in Vercel's environment
const cronSecret = process.env.CRON_SECRET;
// 3. Strict comparison! If the key doesn't match, deny access
if (!cronSecret || authHeader !== `Bearer ${cronSecret}`) {
return NextResponse.json(
{ success: false, message: 'Unauthorized Request' },
{ status: 401 }
);
}
// 4. Correct key, proceed with core logic
console.log("Authorization successful, executing scheduled task...");
return NextResponse.json({ success: true });
}
Testing and Deployment
After pushing your code to GitHub and completing Vercel's automatic deployment, you can enter your project's dashboard on Vercel.
Click the "Settings" tab in the top menu -> then "Cron Jobs" on the left.
Here, you'll see the list of tasks you configured in vercel.json. Vercel even thoughtfully provides a "Run" button for manual testing, allowing you to trigger your API immediately and verify if CRON_SECRET is working correctly!
With Vercel Cron Jobs, a simple configuration file is all you need to build a full-stack automation service with bank-grade security!
Vercel Cron Jobs Configuration
Vercel Cron Jobs are configured in vercel.json using the crons key.
Basic Configuration
{
"crons": [
{
"path": "/api/daily-task",
"schedule": "0 8 * * *"
}
]
}
Multiple Tasks
{
"crons": [
{
"path": "/api/keep-alive",
"schedule": "*/10 * * * *"
},
{
"path": "/api/daily-report",
"schedule": "0 9 * * *"
},
{
"path": "/api/cleanup",
"schedule": "0 3 * * 0"
}
]
}
Schedule Limits by Plan
| Plan | Min Interval | Max Tasks | |------|-------------|-----------| | Hobby (Free) | 1 day | 1 task | | Pro | 1 minute | Unlimited | | Enterprise | Custom | Unlimited |
API Endpoint Design
// app/api/daily-task/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const authHeader = request.headers.get('authorization');
const cronSecret = process.env.CRON_SECRET;
if (authHeader !== `Bearer ${cronSecret}`) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
);
}
// Execute your scheduled task
console.log('Running daily task at:', new Date().toISOString());
// Example: fetch data from external API
// Example: send email notifications
// Example: clean up old database records
return NextResponse.json({
status: 'success',
timestamp: new Date().toISOString(),
task: 'daily-task'
});
}
CRON_SECRET Environment Variable
# Generate a strong random secret
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Output: a1b2c3d4e5f6... (64-character hex string)
Debugging and Monitoring
Vercel Dashboard
- Go to your project in the Vercel dashboard
- Click Settings → Cron Jobs
- View the execution log for each cron job
- Click Run to manually trigger a task
- Check Functions logs for detailed output
Logging Best Practices
export async function GET(request: NextRequest) {
const start = Date.now();
console.log('[CRON] Starting daily-task');
try {
// Execute task
console.log('[CRON] Task completed in', Date.now() - start, 'ms');
return NextResponse.json({ status: 'success' });
} catch (error) {
console.error('[CRON] Task failed:', error);
return NextResponse.json(
{ status: 'error', message: error.message },
{ status: 500 }
);
}
}
Comparison: All Free Cron Methods
| Method | Free Tier | Requires Server | Best For | |--------|-----------|----------------|----------| | Local Crontab | ✅ Unlimited | ❌ Your computer | Local scripts | | cron-job.org | ✅ Unlimited | ❌ Any public API | Simple webhooks | | GitHub Actions | ✅ 2,000 min/mo | ❌ No server | Repo automation | | Vercel Cron | ✅ 1/day (hobby) | ❌ Deployed on Vercel | Next.js apps |
Practical: Vercel Cron + Database Cleanup
// app/api/cleanup/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
if (request.headers.get('authorization') !== `Bearer ${process.env.CRON_SECRET}`) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
// Delete expired sessions
const { error } = await supabase
.from('sessions')
.delete()
.lt('expires_at', new Date().toISOString());
if (error) {
console.error('Cleanup failed:', error);
return NextResponse.json({ error: error.message }, { status: 500 });
}
return NextResponse.json({
status: 'success',
task: 'cleanup',
timestamp: new Date().toISOString()
});
}
Summary
Vercel Cron Jobs provide serverless scheduled tasks for Next.js apps. Configured in vercel.json, they run in the same serverless environment as your API routes, with no separate infrastructure to manage.
Key takeaways:
- Configure cron jobs in
vercel.jsonwith path and schedule | - Free plan: 1 task per day; Pro: 1 task per minute |
- Always authenticate with CRON_SECRET for security |
- Monitor execution in Vercel dashboard (Settings → Cron Jobs) |
- Use descriptive logging with [CRON] prefix for filtering |
- Perfect for daily cleanup, reporting, and keep-alive tasks |
- Test with the manual Run button in the dashboard |
- Combined with cron-job.org for more frequent tasks on free tier |
What's Next: Real-World Automation
The next chapter covers real-world automation scenarios combining all the tools we've learned.