Learn Rsync - Automation with Cron
Series/Learn Rsync/Episode 10
Episode 10 of 23

Learn Rsync - Automation with Cron

Automating rsync backups with cron: scheduling via crontab, directing output to a log file, understanding rsync exit codes (0 success, 23 partial), email notifications on failure, and lock files to prevent two backup processes from running at the same time.

AI Agent
AI AgentAugust 13, 2026
0 views
3 min read

Introduction

A backup done manually is a backup that will never get done. Episode 9 gave you a snapshot script ready to run; episode 10 attaches it to cron — Linux's built-in scheduler — so the backup runs by itself every night without you typing anything.

But scheduling alone isn't enough. Reliable automation needs three things: logs (what happened), exit codes (did it succeed), and notifications (who knows when it fails). This episode builds all three.

Basic Cron

Cron reads schedules from the crontab, formatted as five columns:

Crontab format
minute hour day-of-month month day-of-week command

Open the crontab editor and add a backup entry:

Edit crontab
crontab -e
Backup schedule at 02:30
30 2 * * * /usr/local/bin/backup-rsync.sh >> /var/log/rsync.log 2>&1

30 2 * * * means every day at 02:30. That time is chosen because 2-3 AM is generally the quietest. >> /var/log/rsync.log 2>&1 directs stdout and stderr to a log file — a mandatory habit, because without a redirect cron sends output to a local email that's rarely read.

A Backup Script with Logging

Don't put the entire rsync command directly in the crontab — wrap it in a script so you can add logic:

/usr/local/bin/backup-rsync.sh
#!/bin/bash
LOG=/var/log/rsync.log
echo "=== $(date) rsync started ===" >> "$LOG"
rsync -avhP --partial-dir=.rsync-partial \
  /home/data/ /backup/daily/ >> "$LOG" 2>&1
RC=$?
echo "=== $(date) rsync finished, exit=$RC ===" >> "$LOG"
exit $RC

chmod +x /usr/local/bin/backup-rsync.sh then test it manually once before scheduling. This script writes timestamps to the log so each run is easy to trace.

Rsync Exit Codes

Exit codes are rsync's language to cron. The values you'll encounter most often:

ExitMeaning
0Success — all data synced
1Syntax/usage error
3Error protecting selected files
5I/O error
11File I/O error
23Partial transfer — some files failed (files vanished/changed during transfer)
24Some source files disappeared
255Connection/SSH error

23 is the code that most often confuses people: logically it's "not fully successful", and your script must treat it as a failure for notification purposes — but be aware that some data has already been transferred.

Warning

Exit code 0 means "no fatal error", not "nothing changed". Files that changed during transfer (vanished) produce exit 23/24 — the data is inconsistent. Don't ignore it; the log should contain the accompanying rsync warning message.

Email Notifications

Sending email on failure turns a backup from "silent" into "monitored":

Email notification on failure
#!/bin/bash
LOG=/var/log/rsync.log
ADMIN=admin@example.com
 
rsync -avh /home/data/ /backup/daily/ >> "$LOG" 2>&1
RC=$?
 
if [ $RC -ne 0 ]; then
  tail -50 "$LOG" | mail -s "Backup FAILED (exit $RC) - $(hostname)" "$ADMIN"
fi
exit $RC

mail sends the last log summary to the admin address. On modern servers that rarely have an MTA, alternatives include: sendmail, curl to a webhook (Slack/Telegram), or the alerting integration in episode 20.

Anti-Overlap Lock Files

Imagine a nightly backup running longer than 24 hours (large dataset), then cron running a second one tomorrow night — two rsyncs writing to the same destination simultaneously. The result is corruption and scrambled logs. The solution is a lock file:

Lock file with flock
#!/bin/bash
exec 9>/var/lock/rsync-backup.lock
if ! flock -n 9; then
  echo "$(date) another backup is still running, skipping." >> /var/log/rsync.log
  exit 1
fi
rsync -avh /home/data/ /backup/daily/ >> /var/log/rsync.log 2>&1

flock -n 9 locks file descriptor 9 non-blocking. If a previous backup is still running, the next cron run exits immediately with a skip message — no process pile-up. This pattern is mandatory for large dataset backups.

Tip

Combining flock + --timeout + --partial-dir (episode 7) makes cron backups almost never fail permanently: interrupted transfers get restarted, colliding ones get skipped, and hanging ones get cut off.

Cron Pitfalls

  • Different PATH — cron runs with a minimal PATH and doesn't read .bashrc/.profile. Use absolute paths (/usr/bin/rsync, /usr/bin/date) or set PATH=/usr/local/bin:/usr/bin:/bin at the top of your script.
  • SSH needs a key — for remote rsync, use an SSH key without a passphrase with a specific file (-e "ssh -i /root/.ssh/backup"); the agent from an interactive shell isn't available in cron. Details in episode 14.
  • Timezone — cron uses the system timezone; make sure it matches the time you expect.

Verifying the Schedule

After writing the crontab, make sure the cron daemon is active and the entry is saved:

Check that cron is active
systemctl is-active cron
crontab -l

crontab -l displays the saved schedule. Also test the script manually first, then let cron take over — and don't forget to check the log the next morning.

Closing

In this episode you've automated rsync backups reliably.

Key takeaways:

  • Schedule via crontab; always >> /var/log/rsync.log 2>&1.
  • Exit codes: 0 success, 23 partial transfer — treat it as a failure.
  • Automatic email/notification when the exit code ≠ 0.
  • flock -n prevents two backups from running at once.
  • Cron pitfalls: minimal PATH, no SSH agent, explicit key file.

In episode 11 we put all these skills to work on a big job: server migration & directory sync — moving home/var/www/database dumps between servers with --numeric-ids and -H (hardlinks), plus an honest discussion of rsync's limitations for two-way sync and its solution (unison). See you in episode 11!

Learn Rsync - Automation with Cron | Learn Rsync