Home › Cron Jobs with Node.js

Cron Jobs with Node.js

Two approaches: run a Node script from crontab, or use node-cron to schedule jobs inside your application. Both have valid use cases.

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.

npm install node-cron

Basic usage:

const cron = require('node-cron');

cron.schedule('0 2 * * *', () => {
  console.log('Running daily backup at 2 AM');
  // your task here
});

// With timezone
cron.schedule('0 9 * * 1-5', () => {
  sendWeeklyReport();
}, {
  timezone: 'Europe/Madrid'
});

With TypeScript:

import * as cron from 'node-cron';

cron.schedule('*/5 * * * *', (): void => {
  processQueue();
});

node-cron vs node-schedule vs cron

LibrarySyntaxSeconds supportTimezoneBest for
node-cron 5-field + optional seconds Yes (6-field) Yes Simple recurring tasks
node-schedule Cron strings or JS objects Yes Yes Complex schedules, one-time jobs
cron (kelektiv) 6-field (seconds first) Yes (always) Yes Production apps, robust error handling

Things to Watch Out For

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('...', async () => { await myAsyncTask(); }). Unhandled promise rejections inside cron callbacks will crash your app in newer Node versions.

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.