Home › Node.js Cron Jobs
Node.js Cron Jobs
How to run scheduled tasks in Node.js. Two approaches: the node-cron npm package for in-process scheduling, or running a Node script directly from the OS crontab. This guide covers both, plus node-cron timezone support, TypeScript usage and the node-cron vs node-schedule comparison.
Option 1: Run a Node Script from Crontab
The simplest approach. The OS cron daemon runs your Node script at the scheduled time. No library needed, no long-running process required.
0 2 * * * /usr/bin/node /home/user/scripts/backup.js >> /var/log/backup.log 2>&1
Use the full path to Node (which node to find it).
If you use nvm, the path changes per version - use ~/.nvm/versions/node/v20.x.x/bin/node or create a symlink.
nvm and cron do not get along
nvm sets up Node paths in your shell profile (.bashrc, .zshrc).
Cron does not load those files, so node is not on the PATH.
The fix: use the full path to the Node binary, or install Node globally with a package manager like apt or brew so it lives at a fixed path like /usr/bin/node.
Option 2: node-cron (In-Process Scheduling)
node-cron runs inside your Node.js application process. The schedule is defined in code using standard 5-field cron syntax. Good when your job needs access to your app's database connections, shared state or other in-process resources.
Install via npm:
npm install node-cron
Basic usage (CommonJS):
const cron = require('node-cron');
// Every day at 2 AM
cron.schedule('0 2 * * *', () => {
console.log('Running daily backup');
runBackup();
});
// Every 5 minutes
cron.schedule('*/5 * * * *', () => {
processQueue();
});
// Weekdays at 9 AM
cron.schedule('0 9 * * 1-5', () => {
sendDailyReport();
});
With TypeScript (ESM):
import * as cron from 'node-cron';
cron.schedule('*/5 * * * *', (): void => {
processQueue();
});
node-cron Timezone Support
node-cron supports timezone configuration via the timezone option. Without it, the schedule runs in the server's local timezone - which may not be what you expect if your server is in UTC.
const cron = require('node-cron');
// Runs at 9 AM Madrid time, regardless of server timezone
cron.schedule('0 9 * * 1-5', () => {
sendMorningReport();
}, {
scheduled: true,
timezone: 'Europe/Madrid'
});
// Other common timezone examples:
// timezone: 'America/New_York'
// timezone: 'Asia/Tokyo'
// timezone: 'UTC'
node-cron uses the IANA timezone database. Pass the full timezone name (e.g. 'Europe/London'), not an abbreviation like 'GMT' or 'CET'.
node-cron vs node-schedule vs cron
Three libraries dominate Node.js cron scheduling. They are not interchangeable - each has a different syntax and set of trade-offs.
| Library | Weekly downloads | Syntax | Seconds | Timezone | Best for |
|---|---|---|---|---|---|
| node-cron | ~2M | 5-field + optional seconds | Yes (6-field) | Yes | Simple recurring tasks, Express apps |
| node-schedule | ~1M | Cron strings or JS objects | Yes | Yes | Complex schedules, one-time future jobs |
| cron (kelektiv) | ~2M | 6-field (seconds first, always) | Yes (always) | Yes | Production apps, robust error handling |
The key difference between node-cron and node-schedule
node-cron only supports recurring schedules defined with cron expressions. It cannot schedule a job to run once at a specific future date. node-schedule supports both cron expressions and date objects, making it the better choice when you need to fire a job at a specific moment in the future (like sending a confirmation email 24 hours after signup). For everything that runs on a regular schedule, both work equally well - pick the one your team already knows.
Common node-cron Mistakes
In-process jobs die with the process
If your Node app crashes, restarts or is redeployed, the scheduled jobs stop.
Use a process manager like PM2 with pm2 start app.js to keep the app running.
For jobs that must survive app restarts, the OS crontab approach is more reliable.
6-field vs 5-field syntax
node-cron supports an optional seconds field as the first field, giving 6 fields total.
'*/10 * * * * *' means every 10 seconds.
'0 * * * * *' means every minute at second 0.
If your expression has 6 fields and does not behave as expected, check whether seconds are being interpreted.
Multiple instances running the same job
If you run multiple Node instances (load balancer, PM2 cluster mode), each one will fire the same scheduled job. Use a distributed lock (Redis, database) or designate one instance as the scheduler.
Async tasks need await
node-cron fires the callback synchronously. If your task is async, make the callback async:
cron.schedule('0 2 * * *', async () => {
await runBackup();
await notifySlack();
});
Unhandled promise rejections inside cron callbacks crash your app in newer Node versions. Always wrap async callbacks in try/catch.
Build your cron expression
node-cron uses the same 5-field cron syntax as Linux. Use the generator, copy the expression directly into your cron.schedule() call.