Rsync a snapshot automatically

Sure.

Here is my script.
(Edited: please skip this. A better version is listed below.)

#!/bin/bash

# rsync the latest snapshot via rsync daemon

# settings
#---------
# the directory to be backed up
dataset='/mnt/tank/example'

# the destination protocol
dest_protocol='rsync:'
# the destination user
dest_user='example'
# the destination host
dest_host='192.168.1.44'
# the destination port
dest_port='873'
# the module and path on the host
dest_path='rocker_backup/docker'
# the password file
dest_pw_file='/path/to/dest.rsync-pw'

# the log directory
log_dir='/path/to/log/rsync-docker'
# the log file name
log_file_name="dsm-rsync"
# number of days to keep log files
logs_keep_for_days='30'

# the recipient of the email in case of error
recipient='example@example.com'


# the process
#------------

# the prefix to snapshots
target_parent_dir="${dataset}/.zfs/snapshot"

# the protocol, user host and port of the destination
dest_prefix="${dest_protocol}//${dest_user}@${dest_host}:${dest_port}"

# current date
ds=$(/bin/date +'%Y-%m-%d')
# current time
ts=$(/bin/date +'%H:%M:%S%:z')

# the log file name
log_file="${log_dir}/${log_file_name}-${ds}.log"

# we keep 2 copies by appending a '0' or '1' to the destination alternatively every day.
d=$(date +%s)
postfix=$(((${d}/86400)%2))
dest="${dest_path}${postfix}"

# clean up old logs
/bin/find "$log_dir" -type f -mtime +$logs_keep_for_days -delete


# log the following output to $log_file
{
echo "===="
echo "destination: '$dest'"
echo "$ts rsync starts."
echo

# find the latest snapshot
eval "files=($(/bin/ls -t --quoting-style=shell-always $target_parent_dir))"
if ((${#files[@]} <= 0)) ; then
	# no snapshot found
	/bin/echo "Error: There is no snapshot in '$target_parent_dir'."
	# send an email to notify the recipient
	/bin/printf "Backup '${dataset}' failed. No snapshot is found." | /bin/mail -s "Backup '${dataset}' failed for no snapshot found" "$recipient"
	exit 1
fi

# latest snapshot found
target="${target_parent_dir}/${files[0]}"

# rsync
/bin/rsync -rlptDvx --delete --password-file="$dest_pw_file" "${target}/" "${dest_prefix}/${dest}"

# capture the exit code
code=$?
# current time
ts=$(/bin/date +'%H:%M:%S%:z')

/bin/echo
if [ $code -eq 0 ] ; then
	# backup succeeded without errors
	/bin/echo "$ts rsync done."
	exit 0
fi

# backup failed
/bin/echo "$ts rsync failed with code $code."

# send an email to notify the recipient
/bin/printf "Backup '${dataset}' failed. Please consult '${log_file}' for details." | /bin/mail -s "Backup '${dataset}' failed" "$recipient"

# log to $log_file
} >> "$log_file" 2>&1

Prerequisites for running the script:

A few notes for fellows using this script:

  1. Adjust the setting variables from line 8 to 31 to fit your particular requirement.
  2. Fill the content of the password file assigned to the variable dest_pw_file on line 21 with the password of the user of the destination host assigned to the variable dest_user on line 13.
    It is the password of the user defined on the rsyncd daemon of the destination host, not the password of the system user of the destination host.
  3. It keeps an exact copy of the target, which means extra files and directories are deleted on the destination.
  4. It cannot keep the owners and group of the files and directories on the destination. They belong to the user connected to destination host instead.
    In order to keep the users and groups of the backup, the dest_user has to be root and change the rsync options on line 81 from ‘-rlptDvx’ to ‘-avx’ or ‘-rlptgoDvx’. For security reasons, I don’t recommend running root across network devices.
  5. Compress option of rsync fails frequently. That’s why I don’t enable it.
  6. As rsync traffic is unencrypted, only run this script in a trusted LAN or over VPN.
  7. It does not back up any external device linked. Remove x from the above options (4) in order to back up external devices mounted on the target.
  8. As it has to access the snapshot directory, the script has to be executed as root.
  9. Due to the fact that this script backs up 2 copies on alternative day, you need to create the destination directories for the variable dest_path on line 19. One with a character ‘0’ appended and another with ‘1’ appended.
    I.e. for the setting “dest_path=‘rocker_backup/docker’”, there should be 2 directories, “docker0” and “docker1” under the rocker_backup module directory.
  10. The directory assigned to the variable log_dir on line 24 has to exist before running the script.
  11. In regions with daylight saving policy, the cron job executing this script should be run once a day at 1 am or later (or whatever daylight saving advancement of your region runs). Otherwise you may end up backing up to the latest backup copy again on the day daylight saving becomes effective and standard time restore. In case anything goes wrong while backing up, the other intact copy is 2 days old.
2 Likes