Automatically Syncing a USB Drive on Linux

In addition to syncing important files with Dropbox, I like to periodically back up all my data to a high-capacity drive.

We’ll use udev to make it happen automatically upon plugging in the drive. It expects instructions in the /etc/udev/rules.d directory, so let’s add a rule file named backup-drive.rules:

It ensures sync-backup-drive-wrapper.sh gets called as soon as the USB drive is plugged in. You’ll want to replace the idProduct and idVendor strings with ones that match your drive. Read them from lsusb output:

$ lsusb
...
Bus 001 Device 078: ID 1058:07a8 Western Digital Technologies, Inc.
...

%k tells udev to pass the device name to the script as an argument. Let’s take a look at sync-backup-drive-wrapper.sh:

Our script should terminate quickly and not tie up the event loop so we use nohup to create a detached process that will live on after the wrapper terminates. If our script does not terminate, Ubuntu will fail to auto-mount the drive. Here’s the child script:

It simply polls /etc/mtab to detect when Ubuntu has finished mounting the drive. Unison then takes care of updating the files on the drive. Unison handles 2-way syncing more intelligently than rsync. In Ubuntu, it’s available by default via $ sudo apt-get install unison.

We’ve used the -batch flag to make unison run without interruption. The -prefer newer and -times=true flags ensures unison deals with conflicts by keeping the file with more recent modification time.

We’re also using notify-send for system-level notifications about when syncing begins and ends. It takes a bit of messing around to get them to display when called from udev so we’ve hidden that complexity in the notify function.

Note: make sure to replace USERNAME='username' with your actual username.

Bonus: Before unplugging your drive, you will want to unmount and spin it down.

The script will assume your drive is named “Backup” but you can pass in any name as a command-line argument. I recommend putting the script in /usr/local/bin. Assuming that directory is part of your $PATH environment variable, you can now call the script like a regular shell command:

$ unmount.sh drive-name