On this page
Everyone can write */5 * * * *. Fewer people can say why their backup job ran on the wrong days, or why a script that works in the shell does nothing under cron. Here is the syntax in one screen, then the traps.
The format
# ┌───────── minute (0-59)
# │ ┌─────── hour (0-23)
# │ │ ┌───── day of month (1-31)
# │ │ │ ┌─── month (1-12)
# │ │ │ │ ┌─ day of week (0-7, 0 and 7 are Sunday)
# │ │ │ │ │
# * * * * * command
| Character | Meaning | Example |
|---|---|---|
* | Every value | * * * * * every minute |
, | List | 0 9,17 * * * at 09:00 and 17:00 |
- | Range | 0 9 * * 1-5 09:00 on weekdays |
/ | Step | */15 * * * * every 15 minutes |
Common examples:
30 2 * * * 02:30 every day
0 0 1 * * midnight on the 1st of each month
*/10 8-18 * * 1-5 every 10 minutes, 08:00 to 18:59, weekdays
Many implementations also accept shortcuts such as @reboot, @hourly, @daily, @weekly, @monthly.
Gotcha 1: day-of-month OR day-of-week
If you restrict both day fields, cron runs the job when either matches, not both. 0 9 13 * 5 does not mean “09:00 on Friday the 13th”. It means “09:00 on the 13th of every month, and also every Friday”. For “the first Monday” style schedules, run daily and test the date inside the script.
Gotcha 2: the tiny environment
Cron starts jobs with a minimal environment: a short PATH, no login shell, often a different working directory. A script that calls node or python may not find it.
Use absolute paths for commands and files, set PATH at the top of the crontab, and cd into the right directory inside the job. Test by running the command with a stripped environment (env -i) before you trust it.
Gotcha 3: silent failures
By default, output may be mailed to a local user nobody reads, or dropped. Redirect it explicitly, and make failures visible:
*/5 * * * * /usr/local/bin/sync.sh >> /var/log/sync.log 2>&1
Better still, have the job report success or failure to a monitor (a heartbeat URL or a metrics push), so a job that stops running triggers an alert instead of silence.
Gotcha 4: overlapping runs
Cron does not care that the last run is still going. A job that sometimes takes 12 minutes on a 10-minute schedule will start a second copy and then a third. Guard it with a lock:
*/10 * * * * flock -n /tmp/sync.lock /usr/local/bin/sync.sh
With -n, a second instance exits immediately when the lock is held.
Gotcha 5: time zones and daylight saving
Cron uses the system’s time zone. Around daylight saving changes, a job scheduled in the skipped hour may not run, and one in the repeated hour may run twice. For anything important, run servers in UTC and convert in your head once, not twice a year.
When cron is the wrong tool
| Need | Better fit |
|---|---|
| Retry, backoff, dependencies between jobs | A workflow or job queue system |
| Many machines, run exactly once | A distributed scheduler with locking |
| Event-driven work | Triggers, queues, webhooks |
| Sub-minute schedules | An in-process scheduler or a worker loop |
Do
Treat every cron job as production code: absolute paths, logged output, a lock for long runs, UTC, and a monitor that alerts when it stops.
Don't
Paste a schedule you cannot read back. Explain it in a comment, and check it with a schedule previewer before deploying.
Takeaway
The syntax is the easy part. Cron’s real contract is: run this command at matching times, with a bare environment, and tell nobody what happened. Everything that goes wrong lives in what it does not do for you, so supply those pieces yourself.
Frequently asked questions
What are the five fields of a cron expression?
In order: minute (0 to 59), hour (0 to 23), day of month (1 to 31), month (1 to 12), and day of week (0 to 7, where 0 and 7 are Sunday). A command runs when the current time matches all the fields, with the day-of-month and day-of-week caveat described in the crontab manual.
What does */5 mean in cron?
A step value: every 5th unit of that field. In the minute field, */5 means minutes 0, 5, 10 and so on, every five minutes. The slash sets the step, the asterisk is the full range, and you can combine with a range such as 10-30/5.
Why does my script work in the terminal but fail in cron?
Cron runs jobs with a minimal environment: a short PATH, no interactive shell setup, and a different working directory. Use absolute paths for commands and files, set PATH and variables explicitly in the crontab, and test with the same minimal environment.
What happens if a cron job is still running when the next one starts?
Cron starts another instance anyway. Overlapping runs can corrupt data or pile up. Guard long jobs with a lock, for example flock around the command, so a second run exits or waits instead of running concurrently.
/* Comments */
Comments are offline right now — we reconnect automatically, nothing is lost.