Automated OS X Backup with Launchd

Backup is an oft-discussed topic. Despite this, it is usually put off until some later date. Automating your backup strategy is the most effective way avoiding data loss. In Mac OS X 10.4 Apple implemented a new scheduler daemon — launchd — which makes setting up an automated backup a breeze. There’s an excellent Google TechTalk on launchd if you’re interested.

My backup strategy revolves around automatically running a script every night at 2:00 AM. This script then sends the backups to a remote server for off-site storage. This quick overview will hopefully show you how to get started with your own automated backup strategy.

You’ll be making two files, com.example.backupDaily.plist and backupDaily.sh. The first is a configuration file for the launchd process which will dictate when the script backupDaily.sh should be run.

com.example.backupDaily.plist

This is what my com.example.backupDaily.plist file looks like:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" 
 "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
 <dict>  
     <key>Label</key>
     <string>com.example.backupDaily.plist</string>
     <key>ProgramArguments</key>
     <array> 
         <string>./users/niels/documents/backupDaily.sh</string>
         <string>daily</string>
     </array>
     <key>LowPriorityIO</key>
     <true/>
     <key>Nice</key>
     <integer>1</integer>
     <key>StartCalendarInterval</key>
     <dict>  
         <key>Hour</key>
         <integer>2</integer>
         <key>Minute</key>
         <integer>0</integer>
     </dict>
 </dict>
</plist>

A number of elements in this XML file determine when, what and how your backup script should be run. ProgramArguments determines which file is executed, and in turn the frequency. In this case I’ve set the script to run on a daily basis, and it will execute the file ./users/niels/documents/backupDaily.sh. Finally, the StartCalendarInterval key describes the specifics of when the script is executed, in this case the zeroth minute of the second hour of every day.

If you want to run this script once a month simply change ProgramArguments parameters from daily to monthly and subsequently add a new key under StartCalendarInterval named Day. For example, to run the script on the 28th day of the month, at 4:00 AM, make the following change:


<key>StartCalendarInterval</key>
<dict>  
 <key>Day</key>
 <integer>28</integer>
 <key>Hour</key>
 <integer>2</integer>
 <key>Minute</key>
 <integer>0</integer>
</dict>

Once you’ve got this file set up the way you want it you’ll need to make launchd aware of it. First, move it to /Library/LaunchDaemons/. Do this by issuing the following command in the terminal:


sudo mv com.example.backupDaily.plist /Library/LaunchDaemons/

Then, use the launchctl tool to add it to the scheduler queue. Again, in the terminal:


cd /Library/LaunchDaemons/
sudo launchctl load com.example.backupDaily.plist

Launchd will now execute the script you specified in the plist file at the time you indicated. Now we’ll need to put together our backup script.

backupDaily.sh

The shell script which is called by Launchd can vary from extremely complex shell scripting to simple procedural sets of instructions. I’m not an expert at shell scripting, so I opted to use a simple set of commands I’d otherwise have to issue manually with terminal.

Here’s my backupDaily.sh file. I’ve chosen to back up my e-mail inboxes and accounts:


#!/bin/bash

YEAR=$(date '+%Y')
MONTH=$(date '+%m')
DAY=$(date '+%d')

SOURCE='beregost'
DESTINATION='candlekeep'
DESTINATION_DISK='disk3s3'

diskutil mount /dev/$DESTINATION_DISK
cd /Users/niels/Library/

tar -czf /Volumes/$DESTINATION/$YEAR.$MONTH.$DAY.email.daily.tgz 
 Mail Preferences/com.apple.mail.plist

diskutil eject /Volumes/$DESTINATION

This script defines a number of variables, which determine the date and time, our source volume, destination volume and the disk our destination volume is on (this is required to use the diskutil mount command). It then makes a tar file from the Mail folder and com.apple.mail.plist file and dumps it on our destination disk.

My backup strategy involves keeping a single daily update, which is no older than 24 hours, together with monthly backups which are no older than 28 days. This is not represented in the above example, but is pretty simple to implement according to your own needs.

I also send a compilation of daily backups to a remote server using FTP. The following is simply added to the backupDaily.sh file:


HOST=ftp.example.com
USER=username
PASSWD=password

ftp -n  <<END_SCRIPT
quote USER 
quote PASS 
binary
put monthly.tgz
quit
END_SCRIPT

This will send the file monthly.tgz (which I automatically create along with a number of other files every 28 days) to ftp.example.com over FTP. This method of transmitting backups is not secure, for which you’ll want to use SFTP.

Automating your backup strategy is the best way of making sure that you never lose any of your precious data. I can rest easy at night knowing that I have backups shooting across the world and that a single point of failure will not cause me to lose any significant amounts of data.