Rsync’s link-dest: Not Great for Deployments

TIL: rsync’s --link-dest is pretty bad for deploying code to production servers, unless you can get some fancy copy-on-write going on.

Rsync is probably the best utility to transfer large numbers of files from one location to another, quickly and effectively. The --link-dest argument allows you to hard-link files from a different destination if they haven’t changed, saving both time on transfer, as well as disk space.

It’s perfect for backups, and seemed to me like it would be a good idea for code deployments as well. But I was wrong.

Deploying to production means you have a particular copy lying around, that is not unlikely to change especially in single-server setups, where user actions, such as a WordPress core or plugin update, can lead to changes on the filesystem.

So if you change a file that happened to be hard-linked to other releases lying around for a potential emergency rollback, then you’ve effectively just borked them all :(

$ mkdir -p releases/1
$ echo good > releases/1/wp-config.php

$ rsync -rt --link-dest=../1/ releases/1/ releases/2/
$ rsync -rt --link-dest=../2/ releases/2/ releases/3/
$ rsync -rt --link-dest=../3/ releases/3/ releases/4/

$ ln -sfn releases/4 latest
$ echo bad > latest/wp-config.php

# I screwed up, I'm going to roll back to release 1.
# Which I know was good... Right?
$ cat releases/1/wp-config.php
bad

Next best option is --copy-dest just to speed up transfer, but not preserve disk space.