Deploying WordPress with Capistrano and SVN

Words by Traction Alumni, Faun Winters

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. 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.

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.

Ruby

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.

Rubygems

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:

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.

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, "http://svn.example.com/path/to/htdocs"
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

Debugging

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.

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.