Closest cron expression
0 0 */2 * *
At 00:00, every 2 days in a month. On its own, it runs on 186 days a year.
*/2 in the day-of-month field means odd days: the 1st, 3rd, 5th … 31st. So at the end of a 31-day month, the job runs on the 31st and again on the 1st, two days in a row. The count restarts every month.
Workaround: run a job every other day
For a strict 48-hour rhythm, run daily and skip every second day, using the day count since the Unix epoch:
0 0 * * * [ $(( $(date +\%s) / 86400 \% 2 )) -eq 0 ] && /usr/local/bin/job.sh
The same idea works on every scheduler below: keep the base schedule and put the check at the start of the job’s command or script.
Field by field
| Field | Value | Matches |
|---|---|---|
| minute | 0 |
0 |
| hour | 0 |
0 |
| day of month | */2 |
1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31 |
| month | * |
every value |
| day of week | * |
every value |
Examples
crontab
Run crontab -e and add:
0 0 */2 * * /usr/local/bin/job.sh >> /var/log/job.log 2>&1
For the exact schedule, use the workaround line above instead.
Kubernetes CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: my-job
spec:
schedule: "0 0 */2 * *"
timeZone: "Etc/UTC" # Kubernetes 1.27+
concurrencyPolicy: Forbid # skip a run if the last one is still going
jobTemplate:
spec:
template:
spec:
containers:
- name: job
image: ghcr.io/acme/job:1.0
restartPolicy: OnFailure
GitHub Actions
on:
schedule:
- cron: '0 0 */2 * *' # UTC
GitHub evaluates schedules in UTC, and runs can start several minutes late at busy times.
systemd timer
# /etc/systemd/system/job.timer
[Unit]
Description=Run job.service every other day
[Timer]
OnCalendar=*-*-01/2 00:00:00
Persistent=true
[Install]
WantedBy=timers.target
Enable it with systemctl enable --now job.timer, and check the next runs with
systemd-analyze calendar "*-*-01/2 00:00:00". Persistent=true runs a missed job after
downtime, which cron can’t do.
AWS EventBridge
cron(0 0 1/2 * ? *)
EventBridge cron has six fields (the last is the year), uses ? in either the day-of-month or the
day-of-week field, and numbers weekdays from 1 (Sunday) to 7. Rules run in UTC; EventBridge Scheduler also
accepts a time zone.
Similar schedules: daily
-
Every day at midnight:
0 0 * * * -
Every day at 1 AM:
0 1 * * * -
Every day at 2 AM:
0 2 * * * -
Every day at 3 AM:
0 3 * * * -
Every day at 6 AM:
0 6 * * * -
Every day at 8 AM:
0 8 * * * -
Every day at 9 AM:
0 9 * * * -
Every day at noon:
0 12 * * * -
Every day at 5 PM:
0 17 * * * -
Every day at 11:59 PM:
59 23 * * * -
Twice a day:
0 0,12 * * *
Have a different schedule? Paste any expression into the Cron Expression Generator, or browse all cron examples.
Sources
- POSIX: crontab
- crontab(5) manual page
- Kubernetes docs: CronJob
- GitHub Actions docs: schedule event
- systemd.time(7): calendar events
- AWS docs: EventBridge Scheduler cron expressions
Spotted a mistake? Email hello@devtoolsmith.com and we’ll fix it.
Frequently asked questions
How do I run a cron job every other day?
No single standard cron expression does this exactly. */2 in the day-of-month field means odd days: the 1st, 3rd, 5th … 31st. So at the end of a 31-day month, the job runs on the 31st and again on the 1st, two days in a row. The count restarts every month. For a strict 48-hour rhythm, run daily and skip every second day, using the day count since the Unix epoch: 0 0 * * * [ $(( $(date +\%s) / 86400 \% 2 )) -eq 0 ] && /usr/local/bin/job.sh