Deploying WordPress with Capistrano and SVN

Tuesday, March 8, 2011

Recently, Traction deployed a website built on WordPress to a virtual private server, and being the kind of developer I am, I urged the team to try out Capistrano.

Words by Traction Alumni, Faun Winters

After toying with it for a while, I realized there was no way I could go back to doing things "by hand." Capistrano allows you to push files to a server in a very controlled, repeatable way, greatly reducing the margin of error when deploying to a live site. By reducing the workload required to deploy even very complicated web projects, Capistrano allows you to focus on the thing that's most important—your code.

The initial process of getting Capistrano running was unclear when using WordPress, so I figured I should share what I learned. After playing with it for a while I found it very easy to work with and quite clearly documented behind the scenes. What follows are a few things I've learned along the way.

About Capistrano

Capistrano is a script designed to make repeated deployments trivial, and allows you to run arbitrary shell scripts on the server of your choice in a manner not unlike rake . Capistrano tasks are run from the command line. Consider the following:

cap staging deploy 

The above will run the task default in the namespace deploy on the server staging. For specific details on how to set this up, see deploy.rb below.


Command line

A must for any developer (a Unix/Linux server environment assumed in this tutorial). Some basic WordPress-related info on the UNIX shell here . See also: In the Beginning was the Command Line


Some sort of version control is a requirement for a team working together simultaneously on the same codebase. WordPress can be checked out of Subversion to ensure an easy upgrade process that allows you to update your local installation of WordPress. See Gabriel's blog post about setting up WordPress core as a Subversion external.


If you're on a Mac (like us here at Traction), you most likely already have some version of Ruby installed. Otherwise you can download Ruby . If you're on Windows, I recommend Ruby Installer .


Assuming you're on a Mac, you already have this, otherwise you can download RubyGems . Simply run this command to update to the latest version:

gem update --system 

Capistrano Gem

Originally tool specific to Ruby on Rails , Capistrano has since been modified by the community to include a wide variety of deployment possibilities. It's worth reading about how the original library works before diving into how to override it to do what we want. A wealth of information about the various Capistrano tasks are available on the project wiki , the handbook and the capitate project , although the latter refers to the Rails-specific version of the library, not the generic overrides.

To install Capistrano, run:

gem install capistrano 

Railsless-Deploy Gem

This is the part that makes it possible to use Capistrano with WordPress. By overriding the Rails-specific parts, we gain the ability to deploy many different applications. More info here .

gem install railsless-deploy 

More information about the specifics of modifying Capistrano configuration to work with WordPress can be found here .

Capistrano Multistage Gem (optional)

Using the capistrano-ext gem contains a multistage extension that allows you to deploy across multiple servers in as many stages as you want. At Traction, each developer has their own testing environment, and we also have a staging site for each website in production. Install the gem using:

gem install capistrano-ext 

Then follow the instructions here to set up the folder structure (or see code below) and put in the necessary requires. Capistrano-ext also comes with quite a few other handy tools that are worth exploring, but are outside the scope of this article.

Initialize Capistrano

To initialize Capistrano, in the root of your project (see local directory structure below, this is the folder that contains your htdocs folder), run the following:

capify . 

Uploads Directory and Database Configuration Symlink

In order to maintain uploads across releases, we decided to move the uploads directory outside of the htdocs directory. The easiest way to do that is to symlink to the version in a shared location, that is persistent across deploys. Seen below in the directory structure and in the wordpress:symlinks task, the uploads directory symlink is re-created with every deploy, allowing us to maintain uploads across releases. We placed a Subversion ignore on the contents of the shared directories (do a search for svn propset svn:ignore) in order to allow each developer to also have their own uploads. Since the uploads and the database are connected, each installation should have its own database and uploads folder. To allow each installation to use its own database, our db-config.php file is symlinked to the shared directory, so it can remain outside of version control.

Setting Up WordPress Projects with Subversion (optional)

This is worth doing by itself, as WordPress will stay in sync with your local development and updating to the latest release of WordPress becomes a simple matter of running svn update from your working copy. More info here .

After you have completed the steps in that blog post, from the root directory, run:

cd htdocs mv db-config.php ../shared/db-config.php ln -nfs ../shared/db-config.php db-config.php 

Now take a moment to go edit the db-config.php file in the shared directory with your credentials. Every server to which you will be deploying must have this file configured correctly. Failure to do so will make the deployment fail.

Putting it all Together

Approximate local directory structure:

. |-- Capfile |-- config | |-- deploy | | |-- production.rb | | `-- staging.rb | `-- deploy.rb |-- htdocs | |-- db-config.php -> ../shared/db-config.php | |-- wp | | `-- ... | |-- wp-config.php | `-- wp-content | `-- ... `-- shared |-- uploads `-- db-config.php 

And the associated structure on the server:

/www |-- production | |-- database_backups | |-- htdocs -> ../releases/YYMMDDHHMMSS | | `-- db-config.php -> ../shared/db-config.php | |-- logs | |-- releases | `-- shared | |-- uploads | `-- db-config.php `-- staging |-- export_for_staging.sql |-- htdocs -> ../releases/YYMMDDHHMMSS | `-- db-config.php -> ../shared/db-config.php |-- logs |-- releases | |-- ... | `-- YYMMDDHHMMSS `-- shared |-- uploads `-- db-config.php 


Our Capfile looks something like this:

load 'deploy' if respond_to?(:namespace) # cap2 differentiator require 'capistrano' require 'rubygems' require 'railsless-deploy' load 'config/deploy' 


Our config/deploy.rb looks roughly like the code below. Be sure to fill in the paths and names specific to your application.

set :application, "application name" set :repository, "" set :stages, %w(staging production) set :default_stage, "staging" require 'capistrano/ext/multistage' set :deploy_via, :copy #we are using copy for deployment because our repository is behind a firewall set :scm, :subversion # set :copy_cache, true #this can be used to speed up local checkouts set :copy_exclude, [".svn", ".DS_Store"] set :checkout, "export" set :current_dir, "htdocs" #the directory where you want releases to symlink set :site_root, "#{deploy_to}/#{current_dir}" #wherever you want files to be served from #set :use_sudo, false #can be used if you don't have sudo access, like on a shared host set :keep_releases, 5 before 'deploy:update_code', 'wordpress:symlinks:setup' after 'deploy:symlink', 'wordpress:symlinks:update' after "wordpress:symlinks:update", "deploy:cleanup" namespace :deploy do desc <<-DESC A macro-task that updates the code and fixes the symlink. DESC task :default do transaction do #make sure the release and htdocs directories exists run "mkdir -p #{deploy_to}/releases/", :once => true run "rm -rf #{deploy_to}/htdocs/", :once => true update_code #update the code from the latest version in the repository symlink #see symlink task below end end task :update_code, :except => { :no_release => true } do on_rollback { run "rm -rf #{release_path}; true" } strategy.deploy! end after "symlink" do puts "Symlinking #{current_path} to #{site_root}." run "ln -nfs #{release_path} #{site_root}" end end namespace :wordpress do namespace :symlinks do desc "Setup application symlinks in the public directory" task :setup, :roles => [:web] do run "mkdir -p #{shared_path}/uploads/" end desc "Link public directories to shared location." task :update, :roles => [:web] do #after we have copied the release to the server and symlinked it, we also symlink the uploads directory run "rm -rf #{current_path}/wp-content/uploads/" #symlink uploads in the current release to the one in shared run "ln -nfs #{shared_path}/uploads #{current_path}/wp-content/" # replace the db-config.php file with a link to the one in shared run "ln -nfs #{shared_path}/db-config.php #{release_path}/db-config.php" end end end 


I wrote a little task while I was getting familiar with Capistrano that helped me better understand how my configuration file was being interpreted. YMMV.

namespace :release_info do desc <<-DESC Debugging info about releases. DESC task :default do %w{releases_path shared_path current_path release_path releases previous_release current_revision latest_revision previous_revision latest_release}.each do |var| puts "#{var}: #{eval(var)}" end end end 

Enough to get you started.

Hopefully Capistrano will make deploying WordPress sites easier, especially if you're deploying often. Please let me know in the comments if there is anything that is incorrect or anything that you found particularly useful.

Code examples:

Recent articles
Thursday, January 6, 2011Digging Deeper on Brand APIs

Earlier this week, I wrote an article on Mashable calling for brands to consider developing their own APIs. In it, I used Kraft as an example for how brands could do this.

Tuesday, January 4, 2011Choosy Nerds Choose GIF

There's more than one way to do things from a front end web development perspective. Tables vs divs? Layout and mouseovers in css or javascript? GIF and JPEG and PNG, oh my! Engineering is an art of tradeoffs.

Tuesday, December 14, 2010Unhappy Grouchmas

You're sick of the holidays, aren't you? Awkward holiday parties. Long lines. Commercialism? That's ok. We understand. That's why there's Grouchmas, the holiday for those who've forgotten the meaning of merry.

Traction is a new breed of marketing agency and consultancy—a marketing accelerator for brands and their in-house teams.

© 2000 – 2023


5214F Diamond Hts Blvd #1052 San Francisco, CA 94131 415.962.5800

New business



Privacy Policy