Run cron jobs in Node.js
Schedule Node.js tasks with node-cron, await real completion, and plan for timezones, overlapping runs, restarts, and multiple replicas.
Use node-cron for calendar-based scheduling inside a running Node process. Use a timer when you only need an interval; use an external scheduler or durable job system when execution must survive application downtime.
npm install node-cron
The examples use the v4 API. Check the installed release's Node requirements and migration notes before upgrading from v3.
Clean temporary files after midnight
Save this as cleanup.cjs. The tmp directory must be an application-owned scratch directory; this task intentionally deletes its regular files and does not traverse subdirectories.
const fs = require('node:fs/promises');
const path = require('node:path');
const cron = require('node-cron');
const TMP_DIR = path.join(__dirname, 'tmp');
async function cleanup() {
const entries = await fs.readdir(TMP_DIR, { withFileTypes: true });
let removed = 0;
for (const entry of entries) {
if (!entry.isFile()) continue;
await fs.unlink(path.join(TMP_DIR, entry.name));
removed += 1;
}
console.log(JSON.stringify({ event: 'cleanup.completed', removed }));
}
const job = cron.schedule('0 0 * * *', cleanup, {
timezone: 'Asia/Shanghai',
noOverlap: true
});
job.on('execution:failed', (context) => {
console.error('Cleanup failed:', context.execution?.error);
});
The function returns a Promise covering the actual removals. A completion message is emitted only after they finish; a failed deletion rejects the run instead of producing a misleading success count. Concurrent modification of the scratch directory can still cause failures, so coordinate writers and make retry behavior explicit.
Read the schedule
Five fields mean minute, hour, day of month, month, and day of week. An optional leading field specifies seconds.
| Expression | Schedule |
|---|---|
*/5 * * * * |
Every five minutes |
30 2 * * 1 |
Monday at 02:30 in the configured timezone |
*/10 * * * * * |
Every ten seconds |
In v4, schedule() starts scheduling immediately. To create a stopped task, use createTask():
const job = cron.createTask('0 * * * *', async () => {
console.log('Hourly task started');
});
job.start();
// Later, stop future scheduled runs:
job.stop();
The old scheduled: false option is not the v4 way to do this. Stopping scheduling does not undo work already running. To replace an expression, retire the old task deliberately and create a new one, accounting for any active execution.
Account for downtime and replicas
noOverlap prevents overlapping scheduled executions of that task within one process. It is not a distributed lock. Multiple replicas each register their own schedule; use a single scheduler, shared coordination, or idempotent jobs with durable ownership.
A process supervisor can restart the service, but in-process scheduling does not supply durable replay of runs missed while it was down. For important jobs, persist execution state and design a catch-up policy that handles partial completion and duplicate attempts. A single “last run” timestamp is not always enough.
Use an explicit timezone and test daylight-saving behavior when relevant. Log the scheduled time, timezone, execution identifier, duration, and final result. Test restart and multi-replica scenarios separately. Guance's log collection documentation describes collecting these records; collection alone does not make a job durable.
For system cron, create a separate one-shot entry point that calls cleanup(), handles failure, and exits after completion. The cleanup.cjs example above registers a recurring in-process schedule, so it is not that one-shot entry point. Either approach can import application modules; choose based on process lifetime, ownership, and recovery requirements.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan