Blog   About  Archives

Automating backups using rsync, bash and launchd

One of the residual effects of setting everything up on a new computer is the sudden availability of some old hardware. Normally, there aren’t a whole lot salvage-able components, but luckily I replaced the standard HDD in my old laptop with a Samsung EVO a while back. This presented a perfect opportunity to use it as an external backup drive. In my quest to optimize this, I took a few extra steps to make life super convenient going forward.

The setup

Some basic requirements to get this working:

One of my biggest motivations behind this was to leverage the power of rsync. In a nutshell, it allows for differential copying of your content. It’s open-source and down right awesome!! It’s great for traversing over a large directory structures and updating across entire filesystems.

Getting Started

We’ll start off by installing the latest rsync on our machine. OSX ships with rsync already, but we want to use the newest version. Like any other package, we’ll start with

brew install rsync

We will also need to modify our ~/.zshrc file a little bit to add some details to our path. This is how mine looks at this point

export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/share/python:./jdk-11.0.2.jdk/Contents/Home/bin:$HOME/bin:$PATH 

We can verify that we are running the newest version for rsync now (3.1.3), by simply typing

rsync --verison
Writing the bash script

The basic idea behind my script was to:

Another modification I added was to execute the script only if a particular external drive is mounted. I had to do this primarily because launchd doesn’t offer a way to select which mount to target. More on that in a bit.

Final piece of logic

LOCALMOUNTPOINT="/Volumes/Storage"
if mount | grep "on $LOCALMOUNTPOINT" > /dev/null; then
    echo "Storage is mounted"
    echo "Starting RSync Copy now..."
    rsync -avR ${exclude_flags} ${include_args} ${target_path}
else
    echo "Storage is not mounted"
    echo "Can not do RSync Copy"
fi

My complete script looked like this. The {exclude_flags}, {include_args} and {target_path} are all defined earlier. Also, remember to run

chmod +x <filename_of_backup_script>

to ensure that your script is executable. I also created the destination folder on my external drive with the value that I passed in {target_path}.

I have named my external drive as Storage if it wasn’t obvious already - I know, should’ve come up with a much cooler name, something like Galadriel or Barad-dur - yes, I’m a huge LOTR fan. Anyways, moving on.

Automating the copying part

I did a couple of quick tests and was able to verify that my script was functioning as intended. But I didn’t want to manually run my script every single time I plug in my External Storage Drive. So I decided on using launchd. It’s an open-source service management framework for starting, stopping and managing daemons, applications, processes, and scripts.

First step was to create a plist file in the /Library/LaunchDaemons folder using

sudo touch /Library/LaunchDaemons/BackupDaemon.plist

This created a Global Daemon. This is how my plist file looked like

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
	<key>Label</key>
	<string>BackupDaemon</string>
	<key>Program</key>
	<string>/Users/ramawat/backup_script</string>
	<key>StartOnMount</key>
	<true/>
	</dict>
</plist>

Here, <key>Label</key> specifies the name, which I passed in as the <string> named BackupDaemon. Program specifies the location of the file to be executed. You’ll note that I am using the abolute path and not the relative path - this is key here. And finally, I am specifying when I want this to be run, which is set by <key>StartOnMount</key>. Now, the script will launch everytime any volume is mounted on my computer. But because of the check built in the bash script earlier, the actual copying will only happen when an external volume named Storage is detected.

Once the plist entry was live, I just had to load in launchd. This was accomplished using the following command

launchctl load -w /Library/LaunchDaemons/BackupDaemon.plist

And, that’s it. The automation was successfully set. Now everytime I plug in my external SSD, my backup is ready within seconds. I found this to be a pretty cool learning experience and hopefully this gives you some pointers on setting up some automated backups for your needs as well 😁

Written on Mar 13, 2019.