Developers · 12 October 2026 · 3 min read

Why Your Cron Job Didn’t Run: 8 Common Mistakes

Cron jobs fail silently. The usual causes: time zones, PATH, permissions, percent signs, day-of-week logic and daylight saving time, with fixes from real server administration.

Cron is one of the oldest and most reliable tools on a Linux server, and also one of the most frustrating, because when a job does not run, nothing tells you. No error on the screen, no email unless you set one up. Here are the mistakes we run into most, roughly in order of how often they happen. You can check any schedule in the cron expression generator, which shows the next run times.

1. The wrong time zone

Cron uses the time zone of the server, and cloud servers, containers and CI systems such as GitHub Actions almost always run in UTC. A job scheduled for 0 9 * * * runs at 09:00 UTC, which may be the middle of the night or the afternoon for you. Check with timedatectl or date on the server, and think in UTC when writing schedules for servers.

2. A different environment

Cron does not load your shell profile. The PATH is minimal, usually just /usr/bin and /bin, and variables you set in .bashrc do not exist. A script that works perfectly in your terminal fails under cron because it cannot find node, docker or aws. Use absolute paths everywhere, or set PATH at the top of the crontab.

3. No output, so no clue

By default, cron tries to email the output to the user, and on most servers there is no mail system, so the output disappears. Always redirect: /opt/scripts/backup.sh >> /var/log/backup.log 2>&1. The 2>&1 part matters: it captures error messages too, which is usually what you need.

4. Permissions and the executable bit

If the script is not executable, cron cannot run it directly. Run chmod 755 script.sh or call it through the interpreter: /bin/bash /opt/scripts/backup.sh. Also check which user owns the crontab. A job in root’s crontab and a job in www-data’s crontab have very different access to files. The chmod calculator explains the modes if they are unclear.

5. The percent sign

This one surprises even experienced administrators. In a crontab line, an unescaped % means “newline”, and everything after it is passed to the command as input. So date +%Y-%m-%d breaks. Escape it as \%, or better, move the logic into a script.

6. Day of month and day of week together

When both fields are restricted, cron runs the job if either one matches. 0 0 1 * 1 does not mean “the first of the month if it is a Monday”. It means every first of the month and also every Monday. To get the AND behaviour, schedule on Mondays and check the date inside the script.

7. Every minute by accident

* 3 * * * looks like “at 3 o’clock” but runs sixty times, every minute from 03:00 to 03:59. For a single run you need 0 3 * * *. A heavy backup started sixty times in parallel is a memorable way to learn this.

8. Daylight saving time

If the server uses a local time zone with daylight saving, a job scheduled at 02:30 may not run at all on the night clocks go forward, and may run twice when they go back. Keep servers on UTC, or schedule important jobs outside the 01:00 to 03:00 window.

How to debug quickly

  • Check that cron is running: systemctl status cron (or crond on Red Hat systems).
  • Look for the attempt in the log: grep CRON /var/log/syslog or journalctl -u cron.
  • Run the exact command with a minimal environment: env -i /bin/sh -c '/opt/scripts/backup.sh'.
  • Temporarily schedule it every minute to test, then set the real time.

Our take

A cron job that silently stops working is worse than one that loudly fails, because you find out when you need the backup. For anything important, add a heartbeat: at the end of the script, ping a monitoring URL (Healthchecks.io, Uptime Kuma and similar tools do this), and get an alert when the ping does not arrive. And for jobs that might overlap, wrap them in flock so a slow run never starts a second copy on top of itself. Those two lines have saved us more trouble than any other cron advice.