Skip to content
CronChief

The 7 most common cron mistakes (and how to avoid them)

Cron is simple until a job silently stops running. Here are the seven traps that catch nearly everyone, and exactly how to avoid each one.

Cron looks trivial, and for the happy path it is. The trouble is that a wrong cron job usually fails silently — no error, no email, just a task that quietly never runs (or runs far too often). These are the seven mistakes that catch nearly everyone.

1. The day-of-month / day-of-week trap

This is the big one. When you restrict both the day-of-month and the day-of-week fields, cron does not AND them — it ORs them.

0 0 13 * 5   →  midnight on the 13th, OR every Friday

People write that expecting "Friday the 13th". What they actually get is a job that runs on the 13th of every month and on every Friday. If you need "the first Monday of the month", cron alone can't express it cleanly — gate it in your script instead (check the date, exit early if it's wrong).

2. Assuming the server is in your timezone

Cron runs in the server's timezone. If your box is on UTC but your business runs in New York, 0 0 * * * fires at 7 or 8 PM local time, not midnight. This bites hardest for business-hours schedules like weekday 9 AM jobs.

Fixes: set CRON_TZ=America/New_York at the top of the crontab (on cron implementations that support it), run the daemon in the right timezone, or do the timezone math yourself. Always confirm the next run times in the timezone you actually mean — the parser lets you pick one.

3. Expecting @reboot to run on a schedule

@reboot does not mean "at midnight" or "daily". It runs once, when the system boots — and never again until the next boot. It's for startup tasks, not recurring ones. If your server rarely reboots, an @reboot job may not run for months.

4. Forgetting that cron has almost no environment

Cron runs with a minimal environment. PATH is usually just /usr/bin:/bin, so a command that works in your shell (node, python3, aws) may not be found by cron.

Fixes: use absolute paths (/usr/local/bin/node), or set PATH explicitly at the top of the crontab. The same applies to environment variables your script relies on — cron won't load your .bashrc or .profile.

5. Discarding output, then wondering why it failed

By default cron tries to email a job's output to the user. On most servers mail isn't configured, so that output vanishes — including the error that would have told you what broke.

Fix: redirect output to a log you control:

0 * * * * /path/to/job >> /var/log/myjob.log 2>&1

The 2>&1 is essential — it captures errors (stderr), not just normal output (stdout).

6. Letting slow jobs overlap

Cron fires on schedule regardless of whether the previous run has finished. A job scheduled every minute that sometimes takes 90 seconds will pile up copies on top of each other, competing for the same resources.

Fix: wrap the job in a lock so only one copy runs at a time:

* * * * * /usr/bin/flock -n /tmp/myjob.lock /path/to/job

7. Uneven steps that don't divide 60

Step values are anchored to 0, so */7 * * * * runs at minute 0, 7, 14 … 56 — and then the next run is minute 0 again, only 4 minutes later. Because 60 isn't divisible by 7, the last gap of each hour is short. Stick to divisors of 60 (5, 10, 15, 20, 30) for evenly-spaced runs, or spell out an explicit list.

The meta-lesson: test before you trust

Almost every one of these fails quietly. Before relying on a schedule, verify it: paste it into the cron parser to confirm the description and next run times match your intent, log the output, and — for anything business-critical — check the first few real runs actually happened. A two-minute check saves a silent month of a job that never ran.