Crontab Syntax: Bi-Weekly Server Backup Blueprint
The 3 AM Panic vs. The 3 AM Peace: Two Sysadmins, Same Tuesday
One sysadmin wakes up at 3:17 AM to a Slack alert: the production database is gone, and the last backup is from 19 days ago. Another sysadmin sleeps through the night because a bi-weekly crontab entry fired at 2:17 AM that exact Tuesday, pushing a clean 47 GB tarball to off-site storage. Same goal, same server, same Tuesday. The difference lives in five little fields inside a crontab file.
If you manage a single VPS—say, a small e-commerce box running PostgreSQL and a Node API—you have probably hit the same wall I hit: cron does not have a native "every two weeks" button. You can say "every Tuesday." You can say "the 1st and 15th of every month." But "every other Tuesday" requires you to think differently about how you generate crontab syntax for backup automation scripts. This guide walks through one real, numbered example so you can copy the logic, adapt the numbers, and stop guessing.
The Wrong Way vs. The Right Way: Understanding Cron's Bi-Weekly Blind Spot
Wrong: Hoping "1st and 15th" Equals Bi-Weekly
A lot of beginners generate a crontab line like this:
0 2 1,15 * * /opt/scripts/backup.sh
That fires at 2:00 AM on the 1st and 15th of every month. Sounds bi-weekly, right? It is not. Count the days between February 1 and February 15: that is 14 days. Now count from February 15 to March 1: that is also 14 days in a non-leap year, but in a leap year it is 15 days. In March, the gap from the 1st to the 15th is 14 days, but from March 15 to April 1 it is 17 days. Your "bi-weekly" backup drifts. Over six months, you might go 17 days between backups—exactly the kind of gap that turns a corrupted table into a business emergency.
Right: Pair a Weekly Cron With a Week-Number Gate
The reliable pattern is to schedule the cron job to run every Tuesday, then let the backup script itself decide whether this particular Tuesday is an "on" week or an "off" week. You do this by checking the ISO week number. Even weeks run the backup; odd weeks skip it. That gives you a true 14-day cadence that never drifts, regardless of month length or leap years.
The Vending Machine Analogy: How Crontab Fields Actually Work
Before we generate the exact syntax, picture a vending machine with five buttons side by side. Button 1 is minute, button 2 is hour, button 3 is day of month, button 4 is month, and button 5 is day of week. You press one option per button (or use * to mean "every option on this button"). The machine fires only when all five buttons align with the current time.
The mistake people make is trying to press the "day of month" button to solve a weekly pattern problem. Day of month thinks in calendar numbers (1 through 31), not in repeating weekly cycles. For bi-weekly logic, you want button 5—day of week—because that is the only field that repeats on a stable 7-day cycle.
Real Numbers on the Table: One Concrete Bi-Weekly Backup Example
Let us build the exact crontab line for this scenario: you have a 47 GB database dump that needs to run every other Tuesday at 2:17 AM. Why 2:17? Because round numbers like 2:00 AM create a thundering herd—dozens of automated jobs across your server fleet all firing at once, fighting for disk I/O. Off-round minutes spread the load.
Step 1: The Cron Line Itself
Here is the crontab entry:
17 2 * * 2 /opt/scripts/bi_weekly_backup.sh
Breaking it down field by field:
- 17 — minute (2:17 AM, not 2:00 AM, to avoid I/O contention)
- 2 — hour (2 AM, low-traffic window for your e-commerce traffic)
- * — every day of the month (we do not care about calendar dates)
- * — every month (backups run year-round)
- 2 — Tuesday (0 is Sunday, so 2 is Tuesday in cron's numbering)
This line fires every single Tuesday at 2:17 AM. That is 52 times per year. But we only want backups 26 times per year. The filtering happens inside the script.
Step 2: The Week-Number Gate Inside the Script
At the top of /opt/scripts/bi_weekly_backup.sh, before any backup logic, add this check:
WEEK_NUM=$(date +%V)
if [ $((WEEK_NUM % 2)) -eq 0 ]; then exit 0; fi
Here is the math: %V returns the ISO week number (1 through 53). The modulo operator % 2 divides that number by 2 and returns the remainder. Even numbers return 0, odd numbers return 1. So if the current week is even, the script exits immediately without doing anything. If the week is odd, the script continues and runs your full backup pipeline.
For our Tuesday at 2:17 AM example: let's say the date is Tuesday, November 12, 2024. The ISO week number for that date is 46. 46 % 2 = 0. The script exits. No backup runs. One week later, Tuesday, November 19, 2024, is ISO week 47. 47 % 2 = 1. The script continues, dumps the 47 GB PostgreSQL database, compresses it to roughly 12 GB with gzip, and transfers it to your off-site S3 bucket. The entire job finishes around 3:42 AM based on your transfer speed of 180 Mbps—well before the 6:00 AM traffic pickup.
Putting Logic in Cron vs. Putting Logic in the Script: A Critical Distinction
The Temptation: Cramming Everything Into the Crontab
You might find forum posts suggesting you calculate specific dates and hardcode them into the day-of-month field, something like:
17 2 1,15,29 * * /opt/scripts/backup.sh
This looks clever until you realize the 29th of February does not exist in non-leap years, the 31st shifts the pattern, and you are manually editing the crontab every two months to keep it accurate. That is not automation. That is babysitting.
The Better Pattern: Cron as a Timer, Script as a Brain
Cron is excellent at one thing: waking up at a scheduled time. It is terrible at conditional logic. So let cron be the alarm clock and let your shell script be the decision-maker. The crontab stays simple and stable—you write it once and never touch it again. All the intelligence about "is this an on week or an off week" lives in a three-line shell script that is easy to read, easy to test, and easy to modify if your bi-weekly cadence ever changes to weekly or monthly.
Testing Your Syntax: The Dry Run vs. The Gamble
The Gamble: Set It and Pray
You drop the crontab line in, save the file, and wait two weeks to see if a backup appears in your S3 bucket. If it does not, you have lost two weeks of backup coverage and you have no idea which part failed—the cron schedule, the week-number check, the database dump, or the S3 upload.
The Dry Run: Prove It Before You Trust It
Run the week-number check manually first:
date +%V
If today is Thursday, November 14, 2024, this returns 46. Now run the modulo check:
echo $(( $(date +%V) % 2 ))
This returns 0, meaning the script would exit early today. Change the system date or use a test variable to simulate an odd week:
WEEK_NUM=47; echo $((WEEK_NUM % 2))
Returns 1. The script would proceed. Now run the full backup script manually with WEEK_NUM forced to an odd number and watch the output. If the 47 GB dump completes and the S3 upload succeeds, you have proven the entire pipeline works end-to-end before cron ever touches it.
To verify the cron schedule itself without waiting two weeks, use a crontab generator or validator tool to parse the expression 17 2 * * 2 and confirm it resolves to "every Tuesday at 02:17." This takes five seconds and catches typos like 17 2 * * 3 (Wednesday) before they cost you a week of missed backups.
What Breaks at 2 AM: Edge Cases That Bite Beginners
Iso Week 53: The Year-End Surprise
Some years have 53 ISO weeks instead of 52. When week 53 falls on an odd number, your script runs an extra backup that year. When it falls on an even number, you skip a week you expected to run. This is not a bug—it is a calendar reality. For a 47 GB backup with 90-day retention, one extra or one missing backup at year-end is acceptable. If it is not acceptable for your compliance requirements, add an explicit override:
if [ "$(date +%V)" = "53" ]; then exit 0; fi
This skips week 53 entirely, keeping your cadence locked to 26 runs per year.
Timezone Drift: The Server That Woke Up in UTC
If your server runs in UTC but your brain thinks in Eastern Time, 2:17 AM UTC is 9:17 PM the previous day in New York. Your "Tuesday" backup might fire on Monday evening from a business perspective. Always check your server timezone with timedatectl before finalizing the hour field, and document the timezone in a comment above the crontab entry:
# Bi-weekly backup: every other Tuesday at 02:17 UTC
17 2 * * 2 /opt/scripts/bi_weekly_backup.sh
The Final Crontab Entry: Copy This, Change Your Numbers
Here is the complete, production-ready setup for bi-weekly server backup automation:
Crontab line:
17 2 * * 2 /opt/scripts/bi_weekly_backup.sh
Script header (first three lines of bi_weekly_backup.sh):
#!/bin/bash
WEEK_NUM=$(date +%V)
[ $((WEEK_NUM % 2)) -eq 0 ] && exit 0
That is it. Cron wakes up every Tuesday at 2:17 AM. The script checks the week number. Even weeks exit quietly. Odd weeks run your full 47 GB backup pipeline. You get 26 reliable backups per year, spaced exactly 14 days apart, with zero manual intervention and zero calendar drift.
The sysadmin who sleeps through the night is not smarter than the one who panics at 3 AM. They just generated their crontab syntax correctly, put the conditional logic in the right place, and tested the math before trusting it with production data.
Frequently Asked Questions
How do I run a cron job every two weeks?
To run a cron job every two weeks, you can use a combination of a shell script that checks the current week number or use the day of the month field to run on specific dates like the 1st and 15th. Standard cron syntax does not natively support an "every two weeks" interval, so a conditional statement inside your backup script is often the most reliable approach.
What is the crontab syntax for a bi-weekly server backup?
A common syntax to simulate a bi-weekly backup is `0 0 1,15 * * /path/to/backup.sh`, which runs the script on the 1st and 15th of every month. For a true 14-day interval, you might use `0 0 * * 0` and add a check in your script to only execute if the current Unix timestamp divided by a week's seconds yields an even number.
How to schedule a backup script every other Sunday in crontab?
You can schedule the task to run every Sunday using `0 0 * * 0`, but you must include a modulo operation in your bash script to skip alternate weeks. For example, checking if the week number is odd or even using the `date` command ensures your backup only triggers bi-weekly on Sundays.
Can I use a crontab generator for bi-weekly schedules?
Yes, many online crontab generators allow you to select specific days of the month to approximate a bi-weekly schedule. However, for a strict 14-day cycle, look for advanced generators that support custom shell commands or conditional logic alongside the standard cron syntax.
How to automate server backups using crontab?
First, create a bash script that handles your server's backup logic, such as using `rsync` or `tar`, and make it executable. Then, open your crontab file with `crontab -e` and add a line defining the schedule, followed by the absolute path to your backup script.
How do I calculate the week number in a cron script for bi-weekly execution?
You can calculate the week number inside your bash script using the `date +%V` command, which outputs the ISO week number. By adding an `if` statement to check if the week number is odd or even, you can easily restrict your backup script to run only every two weeks.
What is the best time to run a bi-weekly server backup?
The best time to run a bi-weekly backup is typically during off-peak hours, such as midnight on weekends, to minimize server load and user disruption. Setting your cron job to run at `0 2 * * 0` (2:00 AM every Sunday) with a bi-weekly check in your script is a highly recommended practice.
How to test a bi-weekly cron job for backups?
To test your bi-weekly cron job, temporarily change the cron schedule to run every minute or every five minutes to verify it fires correctly. Once you confirm the script executes and the backup logic triggers as expected, revert the schedule back to your desired bi-weekly configuration.
Why is my bi-weekly cron job not running?
Your bi-weekly cron job might not be running due to incorrect file permissions, missing environment variables, or errors in the conditional logic of your script. Always check the cron logs located in `/var/log/syslog` or `/var/log/cron` to identify the exact reason the task failed to execute.
How to run a cron job on the 1st and 15th of the month?
To run a cron job on the 1st and 15th of every month, use the syntax `0 0 1,15 * * /path/to/your/script.sh`. This is the most common and reliable way to approximate a bi-weekly schedule without needing complex bash calculations for true 14-day intervals.