How to Prevent Overlapping Rsync Cron Jobs: Ensuring Sequential Execution in Linux


3 views

When scheduling rsync backups via cron, a common challenge arises when the transfer duration exceeds the cron interval. This creates race conditions where multiple rsync processes compete for resources and potentially corrupt synchronization states.

The most reliable approach is implementing a lockfile mechanism. Here's a production-tested bash script:

#!/bin/bash

LOCKFILE="/var/lock/rsync_backup.lock"
TIMEOUT=3600 # 1 hour timeout

(
    if ! flock -n -x -w $TIMEOUT 200; then
        echo "Previous rsync still running or lock timed out after $TIMEOUT seconds"
        exit 1
    fi

    # Your rsync command here
    rsync -avz --delete /source/path/ user@remote:/destination/path/

) 200>$LOCKFILE

For systems without flock, check for running processes:

if pgrep -x "rsync" >/dev/null; then
    echo "Rsync already running, skipping this cron execution"
    exit 0
fi

rsync -avz /source/ user@remote:/backup/

For particularly large transfers that might get stuck:

TIMEOUT=7200 # 2 hours
timeout $TIMEOUT rsync -avz /source/ remote:/backup/

if [ $? -eq 124 ]; then
    echo "Rsync timed out after $TIMEOUT seconds" | mail -s "Backup Alert" admin@example.com
fi

In your crontab (crontab -e):

0 * * * * /usr/local/bin/rsync_with_lock.sh >> /var/log/rsync.log 2>&1

Implement logging and notification for failed or skipped runs:

LOG_FILE="/var/log/rsync_$(date +\%Y\%m\%d).log"
exec >> $LOG_FILE 2>&1
echo "Starting rsync at $(date)"

When scheduling rsync backups via cron, a common pitfall occurs when the previous rsync job hasn't finished before the next scheduled execution starts. This leads to:

  • Multiple rsync processes competing for resources
  • Potential file corruption or incomplete transfers
  • Increased system load and network congestion

The most reliable approach is to implement a lockfile mechanism. Here's a robust bash implementation:


#!/bin/bash

LOCKFILE="/var/run/rsync_backup.lock"
TIMEOUT=3600  # 1 hour timeout

# Check for existing lock
if [ -e "${LOCKFILE}" ] && kill -0 cat ${LOCKFILE}; then
    echo "Rsync backup already running as PID cat ${LOCKFILE}"
    exit 1
fi

# Create lock file
echo $$ > "${LOCKFILE}"

# Ensure lock is removed when script exits
trap "rm -f ${LOCKFILE}; exit" INT TERM EXIT

# Main rsync command
rsync -az --delete /source/path/ user@remote:/destination/path/

# Remove lock
rm -f "${LOCKFILE}"

For more robust file locking, use the flock utility:


#!/bin/bash

(
  # Wait for lock on /var/lock/.rsync_backup.lock (fd 200) for 10 seconds
  flock -x -w 10 200 || exit 1
  
  # Commands to run under lock
  rsync -az --partial --progress /source/path/ user@remote:/destination/path/
  
) 200>/var/lock/.rsync_backup.lock

In your crontab, call the script rather than running rsync directly:


0 * * * * /path/to/rsync_backup_script.sh >> /var/log/rsync_backup.log 2>&1

Another approach is to check for running rsync processes:


#!/bin/bash

if pgrep -x "rsync" >/dev/null; then
    echo "Rsync already running, skipping this execution"
    exit 1
fi

rsync -az /source/path/ user@remote:/destination/path/

For particularly large transfers:

  • Increase the timeout value appropriately
  • Consider using --partial and --inplace rsync flags
  • Monitor transfer speeds and adjust scheduling frequency
  • Implement logging to track execution times