Cron Tips & Best Practices

Essential knowledge for reliable cron scheduling

Day-of-month AND day-of-week = union, not intersection

When both day-of-month and day-of-week are set (not *), cron runs on EITHER matching day. 0 0 15 * 1 runs on the 15th AND every Monday — not "Monday the 15th." This catches many people off guard.

Always know your timezone

Cron uses the system timezone by default. A server in UTC running 0 9 * * * fires at 9 AM UTC, not your local time. Always document which timezone your cron jobs use, and prefer UTC for consistency across environments.

Cron is minute-level only

Standard cron cannot schedule sub-minute intervals (every 30 seconds, every 5 seconds). If you need sub-minute scheduling, use systemd timers, Quartz Scheduler (has seconds field), or a custom loop inside a cron job.

Use full paths — cron has a minimal PATH

Cron runs with a restricted PATH (usually just /usr/bin:/bin). Always use absolute paths: /usr/local/bin/python3 /home/user/script.py instead of python3 script.py. Or set PATH at the top of your crontab.

Always redirect output for debugging

Cron swallows stdout/stderr by default. Add >> /tmp/cron.log 2>&1 to your cron command to capture output. Without this, you will never see why a job failed.

Be cautious with @reboot

@reboot runs once when the cron daemon starts, but behavior varies: some systems run it on boot, others on daemon restart. It is unreliable for critical startup tasks — use systemd services instead.

Test expressions before deploying

Paste your expression into SimpleCronTab to verify the next execution times before putting it in production. A typo like 0 0 */2 * * (every 2 days) vs 0 */2 * * * (every 2 hours) can cause major issues.

Use step values that divide evenly

Step values like */7 or */13 in the minute field create uneven gaps at the hour boundary (since 60 is not divisible by 7). Prefer */5, */10, */15, */20, or */30 for perfectly uniform intervals.

Platform-specific quirks

GitHub Actions schedules can be delayed by minutes during high load. Vercel Hobby plan limits cron to hourly minimum. AWS requires ? in day-of-month or day-of-week. Quartz uses 1-7 for days (1=Sunday), not 0-6. Always check your platform docs.

Timezone changes and DST can cause skips or doubles

During DST transitions, a 2:30 AM job may run twice (fall back) or not at all (spring forward). Schedule critical jobs at times that do not fall in the DST transition window (typically 2-3 AM), or use UTC to avoid the issue entirely.