Developer guide
node-cron in Node.js: Schedule Jobs With Cron Syntax
Node.js has no built-in cron. The simplest answer is node-cron — a tiny library that runs functions on the exact cron expressions you already know. This guide covers install, every-minute and hourly schedules, timezones, the alternatives (node-schedule, the cron package), and the production traps that silently kill jobs.
Written by Benjamin Rotshtein
Updated
How do I schedule a job in Node.js with cron syntax?
Install node-cron and call Cron.schedule(expr, fn) with a standard cron expression — for example * * * * * for every minute or 0 0 * * * for midnight. Pass an options object with a timezone to control when it fires. Keep the process alive and handle errors, because a crashed process silently stops the schedule.
- Install
npm install node-cron- Every minute
cron.schedule('* * * * *', fn)- Timezone
cron.schedule('0 9 * * *', fn, { timezone: 'Asia/Kolkata' })
Install and first job
npm install node-cron
const cron = require("node-cron");
// every minute
cron.schedule("* * * * *", () => {
console.log("tick at", new Date().toISOString());
});That is the whole API surface you need for 95% of cases. The expression is standard cron: minute hour day-of-month month day-of-week, with the 6-field seconds form also supported.
Common schedules
cron.schedule("0 * * * *", () => syncData()); // every hour
cron.schedule("*/30 * * * *", () => ping()); // every 30 minutes
cron.schedule("0 0 * * *", () => dailyReport()); // every day at midnight
cron.schedule("0 0 * * 0", () => weeklyCleanup()); // every Sunday midnight
cron.schedule("0 9 * * 1-5", () => openMarket()); // weekdays at 9 AM
cron.schedule("*/10 * * * * *", () => probe()); // every 10 secondsNote the seconds example: node-cron accepts the 6-field format in the first position, so sub-minute intervals are native here even though Linux cron cannot do them.
Running in a specific timezone
By default node-cron uses the server’s local time — which is UTC on most cloud hosts, whatever your laptop says. Pass a timezone to pin the schedule to a real timezone:
cron.schedule("0 9 * * *", () => sendDigest(), {
timezone: "Asia/Kolkata", // 09:00 IST regardless of server TZ
});
cron.schedule("0 0 * * *", () => nightlyTask(), {
timezone: "Etc/UTC", // pin to UTC explicitly
});Use an IANA identifier ( America/New_York, Europe/Berlin, Asia/Kolkata) — not abbreviations like IST, which are ambiguous and break around DST. The cron timezone guide covers this in depth.
node-cron vs node-schedule vs the cron package
All three schedule jobs in Node; they differ in scope. node-cron is the lightest — cron syntax, timezone option, zero dependencies, perfect for typical periodic jobs. node-schedule shines for one-off runs (scheduleJob(new Date(...))) and recurrence rules that are awkward in cron. The cron package is the heavyweight with more features (including timezone-aware parsing and multiple events). For most services, node-cron is the right default; switch only when you hit its limits.
Production safety: keep the process alive and catch errors
Two rules prevent the classic “node-cron not working in production” reports. First, node-cron is in-process: if the Node process exits, the schedule is gone. Uncaught exceptions are the usual killer, so every callback should fail loudly without crashing:
cron.schedule("0 * * * *", async () => {
try {
await syncData();
} catch (err) {
console.error("hourly sync failed", err);
// alert: notify, push to queue, etc.
}
});Second, keep the process supervised. Run under PM2 or systemd with auto-restart so a crash or a deploy brings the scheduler back. If you need the job to survive even a dead process, prefer an external scheduler — a real crontab, a Kubernetes CronJob or a cloud function timer.
Frequently asked questions
What is node-cron and how do I install it?
node-cron is a small npm library that runs JavaScript functions on cron schedules using the familiar 5-field (or 6-field with seconds) syntax. Install it with npm install node-cron, then call cron.schedule(expression, callback). It works in plain Node.js, Express, and any long-running process that stays alive.
How do I run a function every minute in node-cron?
cron.schedule('* * * * *', () => { ... }) runs the callback once per minute. For every 30 minutes use '* /30 * * * *' (no space: '*/30 * * * *'); for every hour use '0 * * * *'. node-cron also accepts a seconds field, so '*/10 * * * * *' runs every 10 seconds.
How do I set the timezone in node-cron?
Pass a timezone option as the third argument: cron.schedule('0 9 * * *', fn, { timezone: 'Asia/Kolkata' }) runs at 09:00 IST. The default is the server's local time. Use an IANA timezone name (not an abbreviation like IST or UTC+5) so DST transitions are handled correctly.
node-cron vs node-schedule vs the cron package: which should I use?
node-cron is the lightest and most popular for standard cron syntax and a timezone option. node-schedule is better for one-off times and more complex recurrence rules. The 'cron' package is a heavier full scheduler with many options. For most apps, node-cron covers everything: stable 5/6-field expressions, timezone support and zero dependencies.
Why does my node-cron job stop after a while or never run in production?
node-cron schedules timers inside your Node.js process — if the process exits (uncaught exception, deployment restart, or the server shutting down idle), the schedule dies with it. Production fixes: wrap callbacks in try/catch so errors cannot crash the process, log failures, keep the process alive (no early process.exit), and use a process manager like PM2 or systemd to restart it on failure.
How do I catch errors in node-cron jobs?
node-cron does not catch callback exceptions for you; an uncaught error inside the callback can crash the process. Wrap the body in try/catch (or .catch for promises) and log the error, optionally with retry or alerting. This is the single most common production failure for node-cron jobs.
Related guides
Build a schedule that survives
Generate the cron expression in plain English and preview the next fire times before pasting it into node-cron — the same syntax works everywhere.