One-Step Symlink Switch

This is a trick I use when deploying websites so I thought I'd post it here for posterity. Actually, technically I stole it from someone else but for now let's pretend it's mine (thanks @__kb!)

When I deploy an application, which is almost invariably a PHP application, I like to put a whole new version of the code alongside the existing one that is in use, and when everything is in place, simply switch between the two. As an added bonus, if the sky falls in when the new version goes live, the previous version is uploaded and ready to be put back into service. In order to be able to do this, I have my document root pointing at a symlink, let's say it is called "current". (disclaimer: I have no knowledge of non-linux operating systems, this post is linux-specific)

When it is time to deploy, I place the new code onto the server, and create two new symlinks, one called "previous" which points to the same location as the "current" symlink does (bear with me) and one called "next" which points to the location of the new code. To deploy, all I need is this:

mv -fT next current

The f forces mv to overwrite the target if needs be, and the T directs mv to consider the second argument as a normal file, rather than as a directory to copy in to. The neat thing about doing it this way is that it happens in a single move, no weird results for people who manage to hit your site while you are typing the new symlink command or during the code updating. It is also just as simple to roll back from this, since you have a symlink pointing to the previously used code version.

I thought I'd share this snippet as it is a handy inclusion in deployment scripts/strategies. What are your tips for managing code deployment?

9 thoughts on “One-Step Symlink Switch

  1. I use phing and generally have a staging directory on the server where I can verify the changes. Then I run 'phing deploy' when it's ready to go. This handles changing the .htaccess file to point at a "website is down" page, runs the db migration script and pushes out the new code.
    Definitely going to have to remember the symlink trick though.

  2. this is a great tip. i normally just use Capistrano do the deployments for me, which does something similar.

    I use capistrano because it's easier for me, and I needed an excuse to learn ruby :).

  3. Nice to see a post about this. :-) 100% of our deploys are done using symlinks.

    We copy our releases into revision-specific folders (which are rev123, rev124, etc.).

    Deploy is done using:

    cd /software-revisions-folder
    ln -sf rev124 current

    To revert: ln -sf rev123 current

    This means you might need to cleanup old releases from your production server though.

  4. This is a convenient way to upgrade, and is certainly a good thing for development / testing environments. On a busy site there might be a slim risk this technique could be problematic. Although the file system is updated in an atomic transaction, a web browser could still read from both the old and new versions of the site.

    For example an unfortunate user might request a JavaScript file from the "current" release, because an HTML document from the previous "current" release referenced it. If this is a concern, steps could be taken to make the JavaScript code backwards compatible.

    Also a resource removed by the "next" release, may result in an HTTP 404 error. Whether that actually breaks anything depends entirely how your website works though.

  5. [geshi lang=sh ln=n]
    Thanks for this tip, Lorna. It really helped us to come up with a release procedure that switches releases instantaneously.

    It would be really useful to see an example of this technique in your post. This is what we came up with using the release date and an integer to label each release:

    Move to the host directory:
    $ cd /path/to/hosts

    Check what the next release number should be:
    $ ls -al releases

    Prepare code from staging to be the next release:
    $ rsync -a --exclude=.svn /path/to/hosts/staginghost/ /path/to/hosts/releases/example.com-YYYYMMDDVV

    Sanity check that the new release is ready:
    $ ls -al releases

    Prepare a symlink to the next release:
    $ ln -s releases/example.com-YYYYMMDDVV example.com_next

    Switch symlinks to make new release live:
    $ mv -fT example.com_next example.com
    [/geshi]

  6. disclaimer: I am not underestimating the universe's ability to produce idiots, the point I'm trying to make is that I haven't managed to make any deploy mistakes using this approach. Yet. Once upon a time, a long time ago, I went onto a conference sta

  7. Thanks for this Lorna!

    I played around with a couple different strategies, and I settled on doing the following:
    Create "prev" and "next" directories that both contain code, with "www" being a symlink which Apache points to as the DocumentRoot

    - Use the "next" folder:
    [code]ln -fns next www[/code]

    - Use the "prev" folder:
    [code]ln -fns prev www[/code]

    Works pretty well - thanks for the catalyst :)

Leave a Reply

Please use [code] and [/code] around any source code you wish to share.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>