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