Matt Kirman

Using Capistrano to deploy PHP (or anything else in a Git repository)

Recently I’ve been looking at a way to ease the path of deploying PHP applications to my servers. I eventually settled on the excellent Ruby tool Capistrano which is very popular with the Rails crowd. Now, I’m aware of the irony of using Ruby to deploy PHP but please bare with me.

All of my code is stored in the Git VCS with a bare repository on the server, which is what you should be doing as well. Once you’ve got used to managing your code with Git over SSH you will never want to go back to FTP again. Seriously, try it.

My work-flow is fairly typical: clone the repository onto my local machine; hack away; commit the changes and push the commit back to the server. The problem arises when it comes to deploying this code - I could either set up a post-update hook in the bare repository to pull the changes into the web root or do it manually.

Using a post-update hook is not ideal. For one, I might not want to trigger a git pull every time I push a commit to the server. Furthermore, how would I go about differentiating between staging and production servers? And manually SSH-ing into the server, changing to the correct directory and running a git pull every time is just too long winded and time consuming.

Instead, I looked at a way to automate the process. I’m quite a fan of Ruby, so I naturally turned to a Ruby solution, Capistrano. Plus the blurb on their home page described the exact tool that I was looking for. That’s some really great copy writing. So, without any hesitation, I quickly ran sudo gem install capistrano and started to have a play around.

The first thing you want to do is to create a capfile in the root of your web application. This is to capistrano as a rakefile is to rake and a makefile is to make. Once you’ve got your capfile you can run cap -T to see a list of your Capistrano recipes. By default Capistrano includes the invoke and shell recipes. I’m not going to explain what these do as they are documented well enough elsewhere.

The next thing you want to do is to create your deploy namespace:

set :web_directory, "the deployed file location"
set :branch, "origin/master"

role :server, "your server address"

namespace :deploy do
    desc "Updates the code on the server"
    task :default, :roles => :server, :except => { :no_release => true } do
        run [ "cd #{web_directory}",
              "git fetch origin",
              "git reset --hard #{branch}" ].join("; ")

    desc "Rollback a single commit on the server"
    task :rollback, :roles => :server, :except => { :no_release => true } do
        set :branch, "HEAD^",

Then once you decide that your code is at the right point to be deployed to your web server, simply call cap deploy. That’s it! No more messing around with Git commands on a remote server.

This isn’t an ideal configuration, obviously there ought to be a way to decide which branch/tag is checked out. But, as a 2 minute fix, this’ll do for now.