Why your cron job isn't running — and how to make cron confess

A familiar scene: you set up a cron job — a script that should run every morning. Run it by hand — works perfectly. On schedule — silence. No result, no error, nothing.
Here's the main thing to understand about cron: it is always silent. Success looks like silence. Failure looks like silence. Cron won't show an error on screen — it has no screen. So fixing it by guesswork is pointless — first we'll make it confess what's happening, then walk through the three causes that cover almost every case.
First — give cron a voice
One trick turns an invisible failure into a readable error: save the job's output to a file. Append to the end of the schedule line:
0 8 * * * /home/you/backup.sh >> /tmp/backup.log 2>&1
>> /tmp/backup.log writes normal output to the file, 2>&1 adds the errors to it too. Now every run leaves either a result or an error message in /tmp/backup.log — the very message you "couldn't see." This is logs in miniature: without them, debugging cron is fortune-telling.
Second trick — a test every-minute job, so you don't wait until morning:
* * * * * date >> /tmp/tick.log
Five stars = "every minute." Wait two minutes and peek into /tmp/tick.log. Lines with dates appearing — cron is alive, the problem is in your job (causes 1–2). File empty — cron itself isn't firing (cause 3).
Cause 1: cron lives in a bare environment
The most common reason for "works by hand, not on schedule." When you run the script, it inherits your environment: your configured PATH, variables from your profile, everything the terminal picked up. When cron runs it, the environment is nearly empty: PATH is trimmed to a couple of system folders, your variables don't exist.
How to check: look at the log from step one. A command not found error about node, python3, pnpm — that's it: cron simply doesn't know where the program lives.
How to fix: use the full path to the program. Find it with which node (say, /usr/local/bin/node) and write the full version in the job:
0 8 * * * /usr/local/bin/node /home/you/bot/daily.js >> /tmp/bot.log 2>&1
Cron also won't see profile variables like API keys — load them inside the script itself (read a .env file, for instance) instead of relying on the environment.
Cause 2: the script runs from the wrong folder
Does your script read config.json or write into data/? By hand you run it from the project folder — relative paths work. Cron runs jobs from the home folder — the script looks for config.json there, doesn't find it, and dies.
How to check: the log will show no such file or directory / ENOENT errors about your own files.
How to fix: change into the project folder right in the job, via &&:
0 8 * * * cd /home/you/bot && /usr/local/bin/node daily.js >> /tmp/bot.log 2>&1
Or use only absolute paths inside the script. The first option is simpler and fixes everything at once.
Cause 3: the machine running cron must not sleep
Cron fires a job at the appointed minute on a running machine. Laptop closed, computer asleep — the minute passed, and plain cron simply skipped the job; it won't catch up. If you schedule "every morning" on your own laptop, it will fire exactly on the mornings the laptop happens to be open.
How to check: the every-minute test ticks while you're at the computer, but the "real" morning runs vanish.
How to fix: scheduled jobs belong on a machine that doesn't sleep — a server, or a free host that supports cron/scheduled jobs. It's the same story as the bot that dies when you close your laptop: your laptop is not a server.
Bonus trap while you're editing the crontab: each job is one line, and the file must end with a newline. A job glued to the last line without a trailing Enter gets silently ignored by cron — yes, silently again.
How do I check the schedule syntax?
Five fields — minute, hour, day of month, month, day of week: 0 8 * * * = "at 8:00 every day." Unsure — paste the line into an AI assistant and ask for a plain-language translation. The classic mistake is swapping the minute and the hour.
Cron fired but there's no result — where do I look?
In the job's log (you did add >> file.log 2>&1, right?). If the log is empty even after a confirmed run — check that the script is executable: chmod +x script.sh.
Can I get notified when the job fails?
Yes: at the end of the script, on error, ping something external — a Telegram bot message or an email. The rule of reliable cron jobs: "silence is not news" — let the script announce both success and failure, at least with a line in the log.
Short story-lessons, an agent simulator and daily practice — in our mobile app. Free.





