The short answer
There is no five-field cron expression for every other week. Cron's fields know the minute, hour, day of month, month and day of week, but nothing counts weeks. The standard workaround is to schedule the job every week and let the command run only on alternate weeks:
0 0 * * 1 [ $(( $(date +\%-V) \% 2 )) -eq 0 ] && /usr/local/bin/job.sh
Cron starts the line every Monday at 00:00; the test passes only in even-numbered ISO weeks.
| Field | Value | Meaning |
|---|---|---|
| Minute | 0 |
at minute 0 |
| Hour | 0 |
at midnight |
| Day of month | * |
any day |
| Month | * |
every month |
| Day of week | 1 |
Monday |
How the test works
date +%Vis the ISO week number (01-53).%-Vdrops the leading zero; without it, the shell reads08and09as invalid octal and the test errors out in those weeks.\%is required inside a crontab: cron turns a bare%into a newline and passes the rest as input.-eq 0runs on even weeks; use-eq 1for odd weeks.
One catch: years with 53 ISO weeks (2026 is one) end on week 53 and start on week 1, both odd, so the rhythm slips by a week at the turn of the year. If it must never slip, count weeks since the Unix epoch instead:
0 0 * * 1 [ $(( $(date +\%s) / 604800 \% 2 )) -eq 0 ] && /usr/local/bin/job.sh
That number goes up by exactly one every 7 days forever, so the job alternates strictly. Flip -eq 0 to -eq 1 if it lands on the wrong week.
Other ways
- systemd timer:
OnCalendar=has no week counter either, but a monotonic timer does fixed intervals:OnBootSec=5minandOnUnitActiveSec=2wrun every 14 days after the last run. It drifts by however long boots take and restarts its count after a reboot, so the calendar test is often simpler. - AWS EventBridge:
rate(14 days)runs every two weeks from the moment the rule was created. Create it on the Monday you want.
Variants
| Schedule | Expression |
|---|---|
| Every other Friday at 5pm | 0 17 * * 5 plus the week test |
| Every week, Monday | 0 0 * * 1 |
| Twice a month, fixed dates | 0 0 1,15 * * |
| Every other day | workaround |
If "every two weeks" is really "twice a month", the 1st-and-15th schedule needs no test at all.
Where to write it
- crontab: the line with the test, as above.
- GitHub Actions:
cron: '0 0 * * 1'(UTC) plus a first job that computes$(( $(date +%s) / 604800 % 2 ))(no\%outside crontab) and anif:on the real job that skips it on odd weeks. - Kubernetes CronJob:
schedule: "0 0 * * 1"and the same test at the start of the container command.
Check the weekly base schedule in the Cron Expression Parser.