How to Schedule Cron Jobs with Offset Timing (5-minute Interval Example)


1 views

When working with multiple cron jobs that need to run at regular intervals but require staggered execution times, the standard cron syntax needs some creative tweaking. The classic example is when you have:

Script 1: Data collection (needs to run at :00, :05, :10...)
Script 2: Reporting (should run at :01, :06, :11...)

The standard 5-minute interval in cron is simple:

# Standard every 5 minutes
*/5 * * * * /path/to/script1.sh

But for offset execution, we need to explicitly specify the minutes:

# Script 1 - runs at :00, :05, :10, etc.
0,5,10,15,20,25,30,35,40,45,50,55 * * * * /path/to/script1.sh

# Script 2 - offset by 1 minute
1,6,11,16,21,26,31,36,41,46,51,56 * * * * /path/to/script2.sh

Method 1: Using sleep in wrapper scripts

#!/bin/bash
# script1_wrapper.sh
sleep 60  # Delay by 1 minute
/path/to/script1.sh

Method 2: Advanced cron syntax (Vixie cron)

Some cron implementations support step values with offsets:

# Runs at 1-56/5 (1,6,11...)
1-56/5 * * * * /path/to/script2.sh

When implementing offset cron jobs:

  • Ensure proper error handling in each script
  • Consider using flock to prevent overlapping executions
  • Log execution times to verify the schedule

Example with flock:

*/5 * * * * /usr/bin/flock -n /tmp/script1.lock /path/to/script1.sh
1-56/5 * * * * /usr/bin/flock -n /tmp/script2.lock /path/to/script2.sh

Use this command to check your cron schedule:

# For Vixie cron systems
crontab -l | grep -v '^#' | awk '{print $1}' | xargs -I {} date -d "now + {} minutes" '+%F %T'

When dealing with multiple cron jobs that need to run at 5-minute intervals but require staggered execution times, the standard cron syntax needs careful configuration. This is particularly important when one job (data collection) feeds into another (report generation), but both must maintain their independent schedules.

Here's how to configure both jobs in your crontab:

# Script 1 - Runs at :00, :05, :10, etc.
0,5,10,15,20,25,30,35,40,45,50,55 * * * * /path/to/script1.sh

# Script 2 - Runs at :01, :06, :11, etc. (1-minute offset)
1,6,11,16,21,26,31,36,41,46,51,56 * * * * /path/to/script2.sh

For more complex scenarios, you might want to create wrapper scripts:

#!/bin/bash
# script1_wrapper.sh
/path/to/script1.sh
#!/bin/bash
# script2_wrapper.sh
sleep 60  # Wait 1 minute before executing
/path/to/script2.sh

Then set both wrappers to run at the same 5-minute interval:

*/5 * * * * /path/to/script1_wrapper.sh
*/5 * * * * /path/to/script2_wrapper.sh

To prevent issues when jobs run longer than expected, consider implementing lock files:

#!/bin/bash
# script1_with_lock.sh
LOCKFILE="/tmp/script1.lock"

if [ -e ${LOCKFILE} ] && kill -0 cat ${LOCKFILE}; then
    exit
fi

trap "rm -f ${LOCKFILE}; exit" INT TERM EXIT
echo $$ > ${LOCKFILE}

# Main script execution
/path/to/script1.sh

rm -f ${LOCKFILE}

Add logging to track execution times:

*/5 * * * * /path/to/script1.sh >> /var/log/script1.log 2>&1
1,6,11,16,21,26,31,36,41,46,51,56 * * * * /path/to/script2.sh >> /var/log/script2.log 2>&1

For more precise scheduling, consider systemd timers:

# script1.timer
[Unit]
Description=Run script1 every 5 minutes

[Timer]
OnCalendar=*:0/5

[Install]
WantedBy=timers.target
# script2.timer
[Unit]
Description=Run script2 every 5 minutes with 1-minute offset

[Timer]
OnCalendar=*:1/5

[Install]
WantedBy=timers.target