Five fields, one command
# ┌─ minute (0–59)
# │ ┌─ hour (0–23)
# │ │ ┌─ day of month (1–31)
# │ │ │ ┌─ month (1–12)
# │ │ │ │ ┌─ day of week (0–7, 0 and 7 = Sunday)
# * * * * * command-to-run
Each field accepts a number, * (any), ranges (9-17), steps (*/15, 0-30/10), and lists (1,15). All fields must match for the job to fire.
Recipes you'll actually use
| Expression | Runs |
|---|---|
*/5 * * * * | every 5 minutes |
0 * * * * | top of every hour |
30 3 * * * | daily 03:30 |
0 9 * * 1-5 | weekdays at 09:00 |
0 0 1 * * | first of the month, midnight |
0 0 * * 0 | Sundays at midnight |
@reboot | once, at startup |
The @-shorthands replace all five fields: @daily, @hourly, @weekly, @monthly, @yearly, @reboot.
The traps
- Timezone: system cron uses server time (usually UTC in containers). Modern cronies support
CRON_TZ=Europe/Berlin; otherwise convert by hand. DST transitions double-fire or skip jobs scheduled in the 02:00–03:00 hour. - Environment: cron runs with almost no env — your
~/.bashrcPATH is absent. Use absolute paths (/usr/local/bin/node) andset -e. - Silence is failure hiding: redirect output (
>> /var/log/job.log 2>&1) or use a dead-man's-switch (healthchecks.io pattern) — a job that quietly stopped is the classic 2am discovery. - Overlapping runs: a slow job started every minute will stack copies. Wrap with
flock -n /tmp/job.lock. - Seconds: cron has no seconds field; two jobs per minute need systemd timers or a wrapper sleep.
Build and validate expressions with a plain-English preview in the Crontab Generator.