HOWTO deploy PHP sites with Capistrano 2

When I learned Ruby on Rails, I found out about Capistrano. This whole automated deployment thing seemed pretty neat. But then my Rails project finished, and I was back to working on a PHP site. There I was, coding along in naive bliss.

Then I started working on for the Obama Campaign. Nick and I didn’t have the biggest iron or the most … process. But we did have Capistrano for PHP.

A little light bulb went off in my head.

Heather’s sites were a great candidate for Capistrano deployments. I could stick everything in subversion … develop on my PowerBookMacBook Pro and deploy to the DreamHost web server. Professional deployment techniques for a site with an engineering team of one.

One thing was bothering me, though. The VFC Capistrano script was so much longer than the ones I was using for my Rails site. Longer. More complicated. Less elegant.

We were making our own folder structure:

t =
folder = "#{t.year}"+"#{t.month}"+"#{}"+"#{t.hour}"+"#{t.min}"+"#{t.sec}"

Wasn’t Capistrano supposed to do that for me? Yes, it was. So I started looking around. Everyone was pointing at Jon Maddox’s instructions. Those were a good starting point, but I had to make a few changes. Here’s what I learned.

How to deploy PHP sites with Capistrano 2

  1. Cache your authentication credentials
  2. Configure Capistrano
  3. Run Capistrano’s initial setup
  4. deploy

Cache your authentication credentials

You’re going to want to set up passwordless authentication for both SSH and Subversion. Otherwise, you’ll get prompted every time you check in or deploy. (This also makes using TextMate‘s subversion bundle harder.)

  1. Cache your SSH credentials. Generate your keypair. On your local machine, do this:
    ssh-keygen -t rsa
    chmod -R 700 ~/.ssh

    Repeat this on the web and svn servers.

  2. Upload your public key from your local machine to the web (svn) server:

    ssh "echo '`cat $HOME/.ssh/`' >> .ssh/authorized_keys"

    Do the same thing from the web server to the svn server.

  3. Often, your username on your local machine is not the same as your username on the subversion server. To tell subversion what name to use, edit ~/.ssh/config, adding lines like these:

          User joeblow
          User joeblow

    Test this out by typing ssh If you still get prompted for a password, make sure the permissions are correct on the webserver (chmod -R 700 ~/.ssh).

  4. On the web server, cache the subversion credentials. Just check out the repository:
    svn co svn+ssh://

    Enter your password when prompted. Subversion will cache this information in ~/.subversion/auth/. You can now delete the trunk directory.

Configure Capistrano

Normally, you prepare a project for Capistrano by typing capify. You don’t really need to do that for PHP sites, because you don’t need a two-part Capistrano script. Instead, just create a text file called Capfile that looks like this:

load 'deploy' if respond_to?(:namespace) # cap2 differentiator
Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }

set :use_sudo, false
set :home_path, "/var/www/mysitename"
set :checkout, "export"
set :repository, "svn+ssh://"
role :web, ""

namespace :deploy do
  task :restart do

Run Capistrano’s initial setup

Run the Capistrano setup command: cap setup. This creates the releases/ and shared/ directories on the server.


To get the site up, just run cap deploy.

Tips, tricks and notes

  1. Following DreamHost’s Capistrano instructions, I set use_sudo to false. On a shared hosting site, I didn’t have root. Duh. Why is this the default option on Capistrano? Dunno.
  2. You need to disable Capistrano’s restart task. Since we aren’t using Rails, there was no mongrel, and nothing to restart.

    So I tried this:

    task :restart do

    I still got an error. It turns out you have to specify the Capistrano namespace:

    namespace :deploy do
      task :restart do
  3. What about database passwords for your web site? You don’t want to store those in the subversion repository. As the Cap FAQ explains, stick a configuration file elsewhere on the server, and copy in a post-deploy task.

    task :copy_database_configuration do
      run "cp #{home_path}/config/#{config_file} #{release_path}/helpers/config.php"
    after "deploy:update_code", :copy_database_configuration
  4. If you’re developing with other people, having your SSH script prompt for your SSH username can be helpful:

    set(:user) { Capistrano::CLI.ui.ask("SSH username: ") }
  5. You can configure your script to do different work on staging versus production deployments. There are probably better ways to do this, but here’s what I have working for me. Suppose you have staging and production on the same server:

      set :home_path, "/var/www/production"
      set :home_path, "/var/www/stage"

    By default, cap deploy deploys to stage. To deploy to production, run PRODUCTION=1 cap deploy (assuming bash).

  6. If you’re deploying WordPress, you will want to make sure the uploads directory is writeable. You might also want a special .htaccess file put in place (I use different ones for localhost and production). Here’s how to do this:

    task :after_symlink do
      run "cp #{current_path}/config/show_htaccess #{current_path}/.htaccess"
      run "chmod -R a+w #{current_path}/wp-content/uploads/"

    Of course, your subversion deployment will overwrite your existing uploads directory, so this particular example is of limited value.

More details

Clayton Lengel-Zigich’s tips for using Capistrano with PHP and the Capistrano FAQ are very helpful.

4 Responses to HOWTO deploy PHP sites with Capistrano 2

  1. Thanks for taking the time to note this in detail. It will definitely be useful for my current project. Thanks again.

  2. CHEM_Eugene says:

    Similar manual in russian language there is on

  3. Olivier says:

    You can upload public keys with ssh-copy-id.


    ssh-copy-id user@host

  4. Pingback: HOWTO setup multistage deployment with Capistrano | paul schreiber

Leave a Reply

Your email address will not be published. Required fields are marked *