I’ve started using a backup strategy based on that originally described by Jamie Zawinski and subsequently covered in Jeff Atwood’s What’s your backup strategy? article. It works by incrementally backing up your data to a bootable clone of your computer’s internal drive, in order to replace the internal drive when it fails.

This script is maintained in my dotfiles repo. Please report problems or improvements in the issue tracker.

This post is mainly to document – for myself as much as anything – the process I went through in order to implement an incremental backup strategy in OS X 10.6+. Use at your own risk. Feel free to suggest improvements if you know of any.

Formatting and partitioning the drive

With your backup drive in its enclosure, connect the drive to your Mac and open the Disk Utility application.

  1. Click on the disk’s name. This should bring up a “Partition” tab in the right panel.
  2. Click on the “Partition” tab.
  3. Under “Volume scheme” select the number of partitions you need. Probably “1 partition” if it is to match your internal disk.
  4. Under “Name” enter the volume name you want to use, e.g., “Backup”.
  5. Under “Format” select “Mac OS X Extended (Journaled)”, which is necessary if the disk is to be bootable.
  6. Click “Options” and check that “GUID Partition Table” is selected.
  7. Click “Apply”.

This will format and partition the disk. The partition(s) should now show up in the Finder and on the Desktop.

Enable ownership permissions

The new partition needs permissions to be enabled to avoid chown errors when using rsync. To do this, select the partition and view its information page (using “Get Info” or Command+I). Expand the “Ownership & Permissions” section and uncheck “Ignore ownership on this volume”

Backup script

The backup script uses rsync – a fast and versatile file copying tool – to manage the copying and moving of data between volumes. You need to install rsync 3 (this is easily done using Homebrew: brew install https://raw.github.com/Homebrew/homebrew-dupes/master/rsync.rb). Rsync offers a wide variety of options and only copies the differences between the source files and the existing files in the destination, making it ideal for incremental backups. You can find out more about rsync in the rsync documentation

The following is the contents of a script I’ve named backup. I’m using it to backup all of the data on my internal disk, with a specified set of exceptions contained within a file called .backupignore.

#!/bin/bash

# Disc backup script
# Requires rsync 3

# Ask for the administrator password upfront
sudo -v

# IMPORTANT: Make sure you update the `DST` variable to match the name of the
# destination backup drive

DST="/Volumes/Macintosh HD/"
SRC="/"
EXCLUDE="$HOME/.backupignore"

PROG=$0

# --acls update the destination ACLs to be the same as the source ACLs
# --archive turn on archive mode (recursive copy + retain attributes)
# --delete delete any files that have been deleted locally
# --delete-excluded delete any files (on DST) that are part of the list of excluded files
# --exclude-from reference a list of files to exclude
# --hard-links preserve hard-links
# --one-file-system don't cross device boundaries (ignore mounted volumes)
# --sparse handle sparse files efficiently
# --verbose increase verbosity
# --xattrs update the remote extended attributes to be the same as the local ones

if [ ! -r "$SRC" ]; then
logger -t $PROG "Source $SRC not readable - Cannot start the sync process"
exit;
fi

if [ ! -w "$DST" ]; then
logger -t $PROG "Destination $DST not writeable - Cannot start the sync process"
exit;
fi

logger -t $PROG "Start rsync"

sudo rsync --acls \
--archive \
--delete \
--delete-excluded \
--exclude-from=$EXCLUDE \
--hard-links \
--one-file-system \
--sparse \
--verbose \
--xattrs \
"$SRC" "$DST"

logger -t $PROG "End rsync"

# Make the backup bootable
sudo bless -folder "$DST"/System/Library/CoreServices

exit 0

Adapted from the rsync script at Automated OSX backups with launchd and rsync

This is the contents of the .backupignore file.

.Spotlight-*/
.Trashes
/afs/*
/automount/*
/cores/*
/dev/*
/Network/*
/private/tmp/*
/private/var/run/*
/private/var/spool/postfix/*
/private/var/vm/*
/Previous Systems.localized
/tmp/*
/Volumes/*
*/.Trash

Adapted from the excludes file at Automated OSX backups with launchd and rsync

Every time the script runs, messages will be written to the system log.

Check that the source (SRC) and destination (DST) paths in the script are correct and match the volume name that you chose when partitioning the disk. Wrapping the $SRC and $DST variables in double quotes ensures that the script will work even if your volume names contain spaces (e.g. “Macintosh Backup”).

The command option --exclude-from tells the script where to find the file containing the exclude patterns. Make sure you either have .backupignore in the home directory or that you update this part of the command to reference the full path of the excludes file.

Running the backup script

You can run the script from the command line, or make it executable from the Finder or the Desktop:

  1. Type the following into the command line to ensure that you have permission to execute the script:

    chmod +x /path/to/rsync_backup.sh
  2. Remove the .sh extension from the script.

  3. Create an alias of the script and move it to the Desktop.

  4. Double click the icon to run the backup script.

It’s important to run the script regularly in order to keep the backup in sync with your internal disk. If you have a desktop computer, or you never turn off your laptop, you can automate the running of the script by setting up a cron job.

Checking the disk is bootable

Once you’ve run the backup script, you should test that the backup disk is bootable. To do this, restart your computer and hold down the Alt/Option key. Your backup disk should be presented, with the volume name you chose, as a bootable disk.

When I first booted my backup, the terminal displayed the following line:

dyld: shared cached file was build against a different libSystem.dylib, ignoring cache

According to this article, the fix for this is to update the cache by entering the following into the terminal:

sudo update_dyld_shared_cache -force

That should be everything you need to start implementing an incremental backup strategy when using OS X.