WordPress and Capistrano

In the first part of this tutorial we met Capistrano — a command line utility to deploy your web application. We went through the process of installing Capistrano and “capifying” WordPress, a new look at the WordPress configuration file and the shared uploads directory.

Today we’ll dig deeper into Capistrano and cover three topics that may come in useful for larger projects. We’ll talk about staging, where deploys are made to a testing server by default and on a production server on demand. We’ll also talk about deploying with Git tags instead of the master branch. Finally I’ll show you a trick on how to hide your production database credentials, useful for large-scale projects where many developers are involved.


Recap from the previous tutorial, our deploy.rb file in the config folder does the WordPress deployments for us and here’s what it looked like at the end of that tutorial:

set :application, "your-application-name"
set :repository, "git@github.com:you/your-project.git"
set :scm, :git
set :deploy_to, "/home/path/to/project/"
set :use_sudo, false

set :deploy_via, :remote_cache
set :copy_exclude, [".git", ".DS_Store", ".gitignore", ".gitmodules"]

server "example.org", :app

namespace :myproject do
    task :symlink, :roles => :app do
        run "ln -nfs #{shared_path}/uploads #{release_path}/application/wp-content/uploads"
        run "touch #{release_path}/env_production"

after "deploy:symlink", "myproject:symlink"

The first block sets a few settings for Capistrano, quite self-explanatory, the second chunk configures how deployments are made and what files to exclude. The server command specifies which server we’ll be deploying to and the myproject part creates the symlink for the shared WordPress uploads folder as well as creates an env_production file which will alter the execution of our wp-config.php file.

If there’s some part of the current deployment configuration file that you didn’t understand, you should revisit the previous tutorial where each section is explained in detail. From now on I’ll assume that you’ve got things set up and working, meaning you can use cap deploy to deploy your WordPress application to your production server.

Staging Deploys

As defined by Wikipedia:

Staging site, in website design, is a website used to assemble, test and review its newer versions before it is moved into production.

Often referred to as the testing server or a playground server. The server itself has to be configured as close as possible to the production server so that the web application is tested in the same environment. It’s not a very good idea to test the application on the same server, since if things go wrong (and they will, trust me), you might affect your production website.

Staging in Capistrano is done using the capistrano-ext gem libraries which you should have installed in the first part of this tutorial. The rest is done by simply setting the available stages and the default stage and including the multistage module using require.

What we’ll need to do is adapt the code snippet shown above to different environments — playground (testing) and production. Meaning the env_production file will have to be created on the production stage only, while the playground would need an env_playground file to work correctly. The server will change as well and so will the deploy_to path. So let’s get started.

At the beginning of your deploy.rb file, enable staging, like this:

set :stages, %w(production playground)
set :default_stage, "playground"
require 'capistrano/ext/multistage'

This defines the two stages and sets playground as the default, meaning all cap task commands will be issued to the playground server by default, and to the production server only when we explicitly ask it to with cap production task.

With this setup, multistage will look for and try to include deploy/playground.rb and deploy/production.rb when deploying to playground and production respectively. The deploy directory should be located right next to your deploy.rb file, so create the following structure:

  • project/Capfile is your Capistrano “make” file
  • project/config/deploy.rb is your deployment file
  • project/config/deploy/production.rb is your production configuration file, and
  • project/config/deploy/playground.rb is your playground config file

As I said earlier we’ll have to remove (or comment out) the deploy_to, server and the env_production commands from the main deployment file (deploy.rb) and move them with slight changes to production.rb and playground.rb files. Let’s start with the playground.rb file:

set :deploy_to, "/home/path/to/playground.example.org/"
server "playground.example.org", :app

namespace :myproject_playground do
    task :symlink, :roles => :app do
        run "touch #{release_path}/env_playground"

after "deploy:symlink", "myproject_playground:symlink"

So we’ve set the deploy_to and server to the path and server where the WordPress application should be deployed to. We then created a new task under the myproject_playground namespace that is run after deploy:symlink and creates that empty env_playground file used by wp-config.php.

You might have already guessed the contents of the production deployment file (deploy/production.rb):

set :deploy_to, "/home/path/to/example.org/"
server "example.org", :app

namespace :myproject_production do
    task :symlink, :roles => :app do
        run "touch #{release_path}/env_production"

after "deploy:symlink", "myproject_production:symlink"

Right, just like the playground file with a little modifications. At this point you’ve got everything set up. A main deployment file which defines our stages and other options, creates the symlink to the shared WordPress uploads directory (note that we didn’t remove that part, only the touch command). We’ve also got two files that specify the routines of deploying to the playground and production servers. Let’s now test this out:

cap deploy
cap playground deploy # same as the command above
cap production deploy # deploys to production

cap deploy:rollback # rolls back the playground release
cap production deploy:rollback # rolls back the production release

That’s all there is to it. If the commands above have worked for you then you’ve done everything right. So now you’re free to cap deploy any time you wish without worrying that you might break things on your production server and when everything’s thoroughly tested you can carry out the deployment on production.

If you need more different scenarios between your playground and production servers you’ll add them to playground.rb and production.rb respectively which will be included by Capistrano based on which deployment has been fired.

Now that you’re comfortable with staging, let’s move over to our next topic.

Deploying with Git Tags

A tag in git is basically a reference to a certain commit in the repository. I like to think of tags as milestones (or markers), making it easier to track different versions of your product or project.

Tagging is especially useful in larger products, when more than one developer is committing and pushing to the repository at any given time, and when there’s a chance that somebody would push more changes right when you’re about to deploy to your production server, resulting in untested code ending up on your live application.

You can use tags to label certain commits in your Git repository and then get Capistrano to deploy that specific tag to your playground and production servers, ignoring all changes pushed into the repository which were made after that tag was created. If you’re not using tags for your Git project yet, start by committing your final changeset, pushing it to your repository and finally tagging it with 1.0 like this:

git tag 1.0
git push --tags

Now let’s get Capistrano to read your available tags and ask you to pick one when you’re about to deploy your WordPress application. We’ll use tags in all the stages so this snippet should go to your main deploy.rb file:

set :branch do
    default_tag = `git tag`.split("\n").last

    tag = Capistrano::CLI.ui.ask "Tag to deploy: [#{default_tag}] "
    tag = default_tag if tag.empty?

Note that :branch is just another variable for Capistrano that you can explicitly set to “master” or anything else, just like you set the :deploy_to variable and the others. This snippet though, gets Capistrano to ask which tag to deploy with the latest tag set as the default one. Thanks to Nathan Hoad for this snippet.

Capistrano WordPress Tags

There you go, now every time you ask Capistrano to deploy your application, it will ask you for a tag having the latest tag set as the default one. Sometimes you’d want to test code on your playground server without having to create a tag each time so note that you can just type in master and Capistrano will deploy whatever is in the master branch, i.e. your latest commit.

Hopefully that will make your deploys more secure and easier to manage with smaller risks for failing. Let’s now move over to some database security.

Hiding Your Production Database Credentials

If you’re the only person working on the project you wouldn’t really care if you had your production database credentials (username and password) in your WordPress configuration file, assuming your Git repository is secure of course. But when it comes to dealing with multiple developers on one project, you have to get your production database secured.

Restricting access from anywhere but localhost is a good start, but we really want to get that production and playground usernames and passwords out of sight and out of our Git repository, leaving local credentials only. If you recall from the previous tutorial we did some magic to our wp-config.php file that works with staging and looks like this:

if ( file_exists( dirname( __FILE__ ) . '/../env_local' ) ) {

    // Local Environment
    define('WP_ENV', 'local');
    define('WP_DEBUG', true);

    define('DB_NAME', 'local_db_name');
    define('DB_USER', 'local_db_user');
    define('DB_PASSWORD', 'local_db_password');
    define('DB_HOST', 'local_db_host');

} elseif ( file_exists( dirname( __FILE__ ) . '/../env_playground' ) ) {

    // Playground Environment
    define('WP_ENV', 'playground');
    define('WP_DEBUG', true);

    // ... playground db constants
} else {

    // Production Environment
    define('WP_ENV', 'production');
    define('WP_DEBUG', false);

    // ... production db constants

There are several ways to do this, easiest of which is of course to separate the wp-config.php file into wp-config-production.php, wp-config-playground.php and wp-config-local.php, that way you can get rid of the empty env_local, env_playground and env_production files and have wp-config.php run the checks based on the configuration files existence and then require them.

Here’s the edited wp-config.php file to show you what I mean:

if ( file_exists( dirname( __FILE__ ) . '/wp-config-local.php' ) ) {

    // Local Environment
    define('WP_ENV', 'local');
    define('WP_DEBUG', true);

    require( 'wp-config-local.php' );

} elseif ( file_exists( dirname( __FILE__ ) . '/wp-config-playground.php' ) ) {

    // Playground Environment
    define('WP_ENV', 'playground');
    define('WP_DEBUG', true);

    require( 'wp-config-playground.php' );

} elseif ( file_exists( dirname( __FILE__ ) . '/wp-config-production.php' ) ) {

    // Production Environment
    define('WP_ENV', 'production');
    define('WP_DEBUG', false);

    require( 'wp-config-production.php' );

The database credentials themselves go into their respective configuration files, while other configuration constants like WP_DEBUG, WP_ENV and WP_CACHE stay in your main wp-config.php file which is under source control. Additionally you should ask .gitignore to ignore your extra WordPress configuration files so that they don’t end up in the Git repository.

At this point you can create a wp-config-local.php file with your local database credentials right next to wp-config.php on your local environment, so the application should now work locally. Next, using SSH sign in to your playground server and browse to the shared folder which contains stuff that is shared between Capistrano releases. If you followed all the previous parts and the previous tutorial you should see the uploads folder there.

Create a new file called wp-config-playground.php and fill it in with your playground database credentials. Next, edit your Capistrano playground.rb file and remove the touch command that adds the env_playground empty file since it is now redundant. Instead of that we’ll create a symlink to our playground configuration file, like this:

namespace :myproject_playground do
    task :symlink, :roles => :app do
        run "ln -nfs #{shared_path}/wp-config-playground.php #{release_path}/application/wp-config-playground.php"

after "deploy:symlink", "myproject_playground:symlink"

Test it out on your playground server by deploying and if it works as expected, move over to your production server and carry out the same thing, replacing playground with production in the task namespace, the configuration files and the deploy:symlink hook.

With this approach Capistrano will copy the wp-config-playground.php and wp-config-production.php files when deploying WordPress to your playground and production servers respectively. This means that both configuration files will not be present in your source control and your team mates will not have access to them.

Note that not so sensitive constants should remain in your main WordPress configuration file, since developers might need to add, remove or modify some of them at some point and you don’t want them bugging you to do it on the remote servers.


This brings us to an end of the second part of Deploying WordPress Like a Pro series, where we talked about Capistrano’s multi-stage extension for better code testing before going live, deploying WordPress based on Git tags for more organized and secure deploys and database credentials hiding for better security in larger teams and projects.

Thank you for reading and hope you’ve learned something new today. We really appreciate your feedback, so feel free to ask us any WordPress and Capistrano related questions or just share your thoughts using the comments section below. Don’t forget to subscribe to our feed too!