Cron Jobs Are Simple Until They Break
Cron is one of the oldest and most reliable tools in Unix. It's also one of the most silently dangerous. When a cron job fails, nothing tells you. No alert, no notification, no error page. The job just... doesn't run. And you don't find out until a customer asks why their report hasn't arrived in 3 days.
Here are the 7 most common crontab mistakes that cause production failures — and how to fix each one.
1. PATH Isn't What You Think
Cron runs with a minimal PATH — usually just /usr/bin:/bin. If your script calls node, python3, docker, or any command not in those directories, it fails silently.
# ❌ This fails — cron can't find node
* * * * * node /opt/app/cleanup.js
# ✅ Use the full path
* * * * * /usr/local/bin/node /opt/app/cleanup.js
# ✅ Or set PATH explicitly
PATH=/usr/local/bin:/usr/bin:/bin
* * * * * node /opt/app/cleanup.js
Fix: Always use absolute paths for commands, or set PATH at the top of your crontab.
2. Timezone Confusion
Cron uses the system timezone by default. If your server is UTC but you schedule a job for "9am," it runs at 9am UTC — not 9am in your local time. This is especially problematic with cloud servers, which are typically UTC.
# ❌ Ambiguous — what timezone is "9am"?
0 9 * * * /opt/app/daily-report.sh
# ✅ Set timezone explicitly (if your cron supports it)
CRON_TZ=America/New_York
0 9 * * * /opt/app/daily-report.sh
Fix: Document which timezone your cron jobs expect. Use CRON_TZ if available, or convert manually. Check with timedatectl.
3. No Output Capture
By default, cron sends output to the local mail spool. If you haven't configured local mail (most modern servers haven't), the output vanishes. Errors included.
# ❌ Output goes nowhere
0 */6 * * * /opt/app/sync.sh
# ✅ Log stdout and stderr
0 */6 * * * /opt/app/sync.sh >> /var/log/sync.log 2>&1
Fix: Always redirect output to a log file. Include 2>&1 to capture errors too.
4. Overlapping Executions
If a job takes longer than its interval, the next execution starts before the previous one finishes. Two instances of the same script running simultaneously can cause data corruption, deadlocks, or double-processing.
# ❌ If this takes > 5 minutes, instances pile up
*/5 * * * * /opt/app/process-queue.sh
# ✅ Use flock to prevent overlap
*/5 * * * * flock -n /tmp/process-queue.lock /opt/app/process-queue.sh
Fix: Use flock (file lock) to ensure only one instance runs at a time. The -n flag means "don't wait, just skip if locked."
5. No Monitoring — The Silent Killer
The #1 cron job mistake: assuming it works because you set it up correctly once. Cron jobs break for dozens of reasons — disk full, permission changes, dependency updates, server migration, reboot without cron enabled.
# ❌ Hope-driven operations
0 2 * * * /opt/app/backup.sh
# ✅ Add dead man's switch monitoring
0 2 * * * /opt/app/backup.sh && curl -s https://cronping.anethoth.com/ping/YOUR_TOKEN
Fix: Add a monitoring ping at the end of every critical cron job. If the ping stops arriving, you get alerted. CronPing does exactly this — set an expected interval, and get notified when a job goes silent.
6. Environment Variables Missing
Cron doesn't load your shell profile (.bashrc, .profile). Environment variables you set in your terminal session don't exist in cron's environment.
# ❌ $DATABASE_URL is empty in cron
0 3 * * * python3 /opt/app/migrate.py
# ✅ Source the env file first
0 3 * * * . /opt/app/.env && python3 /opt/app/migrate.py
# ✅ Or pass vars inline
0 3 * * * DATABASE_URL=postgres://... python3 /opt/app/migrate.py
Fix: Source your environment file or define variables in the crontab itself.
7. Day-of-Week vs Day-of-Month Confusion
Cron's day-of-week and day-of-month fields interact in a confusing way. If both are set, the job runs when either matches — not when both match. This is the opposite of what most people expect.
# ❌ You think: "first Monday of every month"
# Actually: "the 1st of every month AND every Monday"
0 9 1 * 1 /opt/app/monthly-report.sh
# ✅ Use a wrapper script for complex schedules
0 9 1-7 * 1 /opt/app/monthly-report.sh
# This runs every Monday that falls on the 1st-7th (first Monday)
Fix: For complex schedules, use a wrapper script that checks the date before running, or use a scheduling tool with more expressive syntax.
Prevention: Monitor Everything
Every fix above addresses a specific mistake. But the meta-fix is simple: monitor every cron job. If you add a dead man's switch ping to every job, you'll know within minutes when any of these mistakes cause a failure — instead of finding out days or weeks later.
Try CronPing free
Common crontab pitfalls that cause silent failures in production — from timezone confusion to overlapping jobs. Get started with our free tier — no credit card required.
Get started free →