All posts

Crontab Node.js: Correct Syntax and Fixing Silent Failures

August 13, 2026

Running a Node.js script through the system crontab is one of the simplest ways to schedule recurring work — until it silently stops running and nobody notices for a week. This guide covers the exact crontab nodejs syntax, the environment gotchas that make scripts fail without any error message, and how to close the visibility gap once the job is finally wired up correctly.

This is strictly about the OS-level crontab — the cron daemon and the crontab -e file — not an npm scheduler package running inside a long-lived Node process. Those are covered later.

The Right Way to Put a Node.js Script in Crontab

To run a Node script from crontab, you need two absolute paths: one to the node binary, one to your script file. Cron does not know your shell aliases, your nvm setup, or your project's relative directory structure — it only knows what you tell it explicitly.

Open your crontab with crontab -e and add a line like this:

*/5 * * * * /usr/bin/node /home/deploy/apps/reports/index.js >> /home/deploy/apps/reports/cron.log 2>&1

That line runs index.js every five minutes, using the full path to node, the full path to the script, and redirects output to a log file. This is the pattern to memorize: absolute path to node, absolute path to script, output redirection. Skip any one of those and you're likely to run into the failures below.

If your script depends on other files in its directory (reading a config, writing a relative output path), prefix the command with cd so the working directory matches what your code expects:

*/5 * * * * cd /home/deploy/apps/reports && /usr/bin/node index.js >> cron.log 2>&1

Why It Works in Your Terminal but Fails Under Cron

The single most common cause of a Node cron job that "does nothing" is environment mismatch. When you run node index.js in your terminal, your shell has already loaded .bashrc or .zshrc, set a full PATH, and possibly activated nvm. Cron does none of that. It runs each job with a minimal environment — often just SHELL, PATH (something bare like /usr/bin:/bin), HOME, and LOGNAME — and no shell profile is sourced at all.

This is why node: command not found shows up in cron's error mail or logs, but never in your terminal: cron's stripped-down PATH doesn't include the directory where node lives, especially if it was installed via nvm, which puts binaries somewhere like ~/.nvm/versions/node/v20.11.0/bin — a path your shell profile adds, but cron never sees.

To confirm this is what's happening, dump cron's actual environment to a file as a one-off diagnostic job:

* * * * * env > /tmp/cron-env.txt

Compare that output to what env prints in your interactive terminal. The differences — usually a much shorter PATH and missing custom variables — explain most "it works manually but not in cron" reports.

Fixing PATH, node Version, and Environment Variables

Start by finding the exact node binary cron should call. Run which node in the terminal you normally use, and use that full path in your crontab line rather than assuming /usr/bin/node. If you manage versions with nvm, which node typically resolves to something like /home/deploy/.nvm/versions/node/v20.11.0/bin/node — that specific path is what needs to go in the crontab entry, not just node on its own, since nvm-managed installs are never on cron's default PATH.

For environment variables like NODE_ENV or API keys, you have two solid options. First, set them directly at the top of the crontab file:

NODE_ENV=production
API_KEY=your_key_here
*/5 * * * * /usr/bin/node /home/deploy/apps/reports/index.js >> cron.log 2>&1

Second, and often cleaner for anything beyond one or two variables, wrap the invocation in a small shell script that sources your environment, exports the variables, and then calls node with the absolute path — then point crontab at the wrapper instead of at node directly. This keeps secrets and config out of the crontab file itself and makes the job easier to test manually, since process.env inside your script only ever sees what was explicitly exported before node started.

Capturing Output and Exit Codes

Redirection is what turns cron's silence into something inspectable. Appending >> /path/to/log 2>&1 sends both stdout and stderr to the same file, so console.log output and uncaught exceptions both land in one place. Without it, cron either discards output or tries to mail it to a local user account nobody checks.

Exit codes matter too: a Node script that exits with a non-zero code signals failure to cron and to any wrapper script checking $?, while an uncaught exception that isn't handled may leave a hung process or an ambiguous exit status. Checking exit codes in a wrapper is a cheap first line of defense before you reach for anything more sophisticated. For a deeper look at where cron's own logs live and how to read them, see Crontab Log: Where to Find It and How to Read It.

crontab vs. node-cron: Picking the Right Approach

System crontab and an npm package like node-cron solve the same scheduling problem at different layers. Crontab is managed by the OS, survives your Node process crashing or restarting, works regardless of which language the job is written in, and needs no long-running process to stay alive. node-cron runs inside your application, which means the schedule dies the moment that process dies — but it also means you can share in-memory state and skip writing a separate script.

If you're already running a persistent Node server and want lightweight in-process scheduling, the npm-package route may be a better fit; that comparison, including setup and its own reliability tradeoffs, is covered in Cron in Node.js: Setup, Options, and the Reliability Gap. For anything that needs to run independent of a specific app process — batch jobs, reports, cleanup scripts — system crontab is generally the sturdier choice.

The Gap Crontab Can't Fill: Knowing When a Job Fails

Cron itself has no concept of alerting. If your Node script throws, hangs, or simply never fires because of a PATH problem, cron doesn't notify anyone — the job just doesn't appear in the log, and nothing tells you until a downstream report is missing or a customer complains. Even with output redirection working correctly, someone still has to remember to open that log file.

The practical fix is having the script itself report its status: ping a monitoring endpoint when it starts, another when it finishes successfully, and a failure signal if it throws or times out. That turns an invisible cron job into one that actively tells you something is wrong. For teams running several scheduled scripts, this pattern is also central to broader reliability practices, detailed in Cron Jobs in Production: How to Keep Them Reliable.

Once your Node script is correctly wired into crontab with the right paths, environment, and logging, the only remaining question is whether it actually ran when it was supposed to. Add a start/success/fail ping to your script and connect it to Cronevra so a missed run, a timeout, or an uncaught exception triggers an alert instead of disappearing into a log file nobody reads. Check the pricing page to get your first jobs monitored today.

Frequently Asked Questions

Why doesn't my Node.js script run when I add it to crontab even though it works when I run it manually?

Cron runs jobs with a minimal environment that lacks the PATH, shell profile, and variables your interactive terminal loads automatically. If your crontab line calls node without an absolute path, or your script relies on environment variables set only in .bashrc, the job fails silently or throws command not found. Using absolute paths for both node and your script, plus explicit environment variables, resolves this in most cases.

How do I find the correct path to node for use in crontab?

Run which node in the same terminal and user account you normally use to run the script, and use that exact output in your crontab line. If you use nvm, this path will point into a directory like ~/.nvm/versions/node/v20.11.0/bin/node rather than /usr/bin/node, since nvm-managed installs aren't on cron's default PATH.

Can I use npm or environment variables like process.env in a crontab-scheduled Node script?

Yes, but only variables you explicitly export are available — cron doesn't load your shell profile, so anything set only in .bashrc or .zshrc won't reach process.env. Set variables directly at the top of the crontab file, or use a wrapper shell script that exports them before invoking node, to make values like NODE_ENV or API keys reliably available.

Should I use crontab or a package like node-cron to schedule Node.js jobs?

Use system crontab when the job needs to survive your Node process restarting, run independent of any specific application, or potentially be written in another language later. Use node-cron when you already have a persistent Node server and want lightweight in-process scheduling without a separate script — though that schedule disappears if the process crashes.

How do I see errors or output from a Node.js script that crontab runs?

Redirect both stdout and stderr to a log file by appending >> /path/to/log 2>&1 to your crontab line, which captures console output and uncaught exceptions in one place. Without this redirection, cron typically discards output or tries to mail it to a local account nobody checks.

What's the correct crontab syntax to run a Node.js script every 5 minutes?

Use */5 * * * * /usr/bin/node /absolute/path/to/script.js >> /absolute/path/to/cron.log 2>&1, replacing the node and script paths with the output of which node and your script's actual location. The */5 * * * * schedule expression tells cron to run the command every five minutes, every hour, every day.