Background

My current job has me managing a lot of PHP projects. My typical workflow up until recently was to use VS Code tasks to rsync over my changes to our server environments. This process wasn’t ideal for a multitude of reasons, so I decided to change it by using Capistrano as a proper deploy tool that we could then potentially extend for project-specific build tasks, and maybe integrate it into our GitLab instance for CI/CD later down the road.

Capistrano is a “lightweight” server automation tool that is self-described as being between “… simple rsync bash scripts to complex containerized toolchains …“. It is written in Ruby and has simple syntax while being extremely extensible for custom build and deploy tasks. I currently take advantage of its ability for easy deploys and rollbacks, but since tasks are an extension of Rake tasks and are written in Ruby, it can be used for much more.

End Goal

Each person that has deploy privileges is able to push new revisions with the following command:

bundle exec cap [ENVIRONMENT] deploy

where [ENVIRONMENT] is either development or production. My team is just getting started with Capistrano, and so far we only use it for deploys and rollbacks (if necessary). There are many more advanced features that I’m excited to investigate, including parallel deploys and managing git tags.

Setup

One of the bigger downsides to Capistrano is that it’s not the easiest to get up and running if you don’t already have a ruby installation. For developers and administrators on UNIX-like environments this is relatively painless with ruby managers such as rbenv or rvm, but Windows users have a bit of a hurdle to get over before being able to use Capistrano. This pain can be alleviated if these users opt to run a Linux-based VM or use Windows 10’s great Windows Subsystem for Linux, but some users just prefer a native Windows environment.

Prerequisites

This guide assumes the following:

  • You know how to use SSH and have key-based authentication setup with the servers you’d like to deploy to. If you don’t it’s not a problem, but the SSHKit library that Capistrano uses will ask you for a password on every deploy.
  • You have Ruby installed on a UNIX-like environment, or will follow the Windows-specific instructions that follow.
    • If you need to install ruby on Linux or macOS, I recommend a Ruby manager (like the ones mentioned above) to avoid having to install gems at the system-level along with other benefits as described on their respective project pages.

Installing Capistrano

Just run gem install capistrano, or run bundle install in a project directory where Capistrano is included in the project’s Gemfile.

Windows Specific Setup

As mentioned before the Windows setup is a bit more involved if you’re not using a Linux VM or WSL. To deploy natively from Windows you’ll need the following installed:

Ruby Installation and Setup

Make sure you get the latest x64 Ruby version with the devkit. At the time of writing this is v2.6.1-1. Then follow the screenshots below. Note that part of the installation process takes place in CMD and requires you to hit enter twice in order to install and update the MSYS2 dev environment to build ruby’s native C extensions; where this happens is shown in the last two screenshots. After installing you will want to run either PowerShell or CMD and run the two following commands: gem update --system and gem update, answering “y” whenever it asks to overwrite executables. This will update the core gems Ruby ships with.

Ruby Installation Step 1 Ruby Installation Step 2 Ruby Installation Step 3 Ruby Installation Step 4 Ruby Installation Step 5

PuTTY Installation and Setup

If you’re planning on just using the OpenSSH executables that Git for Windows ships with, you can skip this step; otherwise PuTTY installation is fairly straightforward. Note that after installing it is important to run PuTTY and save at least one session in order to get the “Use PuTTY’s SSH” install option from Git for Windows. The session doesn’t have to have any information, just make sure you save it.

PuTTY Installation

Git for Windows Installation and Setup

Follow the screenshots below making sure to keep close attention to the options selected in each step. The step asking to choose a text editor is up to personal preference, I just highlighted nano since it is the most user friendly and doesn’t require any additional installation.

Git Installation Step 1 Git Installation Step 2 Git Installation Step 3 Git Installation Step 4 Git Installation Step 5 Git Installation Step 6 Git Installation Step 1 Git Installation Step 8

Final Steps

If you are using PuTTY with Pageant as your ssh agent, make sure you have private key authentication setup and Pageant running with your keys added. Consult the official Capistrano documentation on Authentication and Authorization for more info on how to set up your ssh agent.

Migrating Projects

Gotchas

It’s important to note that the directory structure of deployed applications will change slightly. Capistrano by default keeps the last 5 deploys of an application and symlinks the latest one to the “current” directory in the deploy path. This allows for easy rollbacks but might cause some issues with applications that keep large binary files or user uploads in its web directory. For applications such as these, you might want to investigate the shared directories functionality of Capistrano that will allow for symlinking of directories rather than deploying copies of large files, as well as git-lfs for keeping tracking of these files in the project’s repo.

Initializing Capistrano

For my projects at work I used the following steps to migrate each application over. Disclaimer: both of the capistrano-simple-* gems were authored by me; I just wrapped some basic reusable tasks into gems for ease of reuse and maintainability. You don’t have to use them in your projects if you don’t find them useful.

  1. Run bundle init in the project’s root directory to get a simple Gemfile
  2. Edit the Gemfile to include Capistrano and (optionally) the two helper gems, which should look like the following:

    # frozen_string_literal: true
    
    source "https://rubygems.org"
    
    gem 'capistrano', '~> 3.11', require: false
    gem 'capistrano-simple-htaccess', require: false
    gem 'capistrano-simple-permissions', require: false
  3. Run bundle install to install the Capistrano gem if it isn’t installed already

  4. Run bundle exec cap install STAGES=development,production to install Capistrano into the project

  5. If you installed the two helper gems, edit the Capfile ​to require them.

    # Load DSL and set up stages
    require 'capistrano/setup'
    
    # Include default deployment tasks
    require 'capistrano/deploy'
    
    # Include other deployment tasks
    require 'capistrano/simple_htaccess'
    require 'capistrano/simple_permissions'
    
    # SCM Config
    require 'capistrano/scm/git'
    install_plugin Capistrano::SCM::Git
    
    # Load custom tasks from `lib/capistrano/tasks` if you have any defined
    Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
  6. Edit config/deploy/production.rb and config/deploy/development.rb to match your servers and give them the “web” role

    # In development.rb
    server 'dev.example.com', roles: %w{web}
    
    # In production.rb
    server 'prod.example.com', roles: %w{web}
  7. Edit config/deploy.rb to set project specific information such as the git repo and directory to deploy. Below is an example for a generic application. You will want to change each set line to match your application’s environment

    # config valid for current version and patch releases of Capistrano
    lock '~> 3.11.0'
    
    set :application, 'application_name'
    set :repo_url, 'git@your-git-site:user/project.git'
    
    # Default branch is :master
    #ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp
    
    # Default deploy_to directory is /var/www/my_app_name
    set :deploy_to, '/var/www/path/to/app'
    # Optional, include if you use the simple-permissions helper gem
    set :permissions_folders, [fetch(:deploy_to)]

At this point the application should be configured enough to deploy. In order to deploy a new application you should remove any files in the current deploy location as the structure will change with Capistrano. Once the directory is clear you can deploy the application using bundle exec cap ENVIRONMENT deploy, where ENVIRONMENT is either development or production.

Preventing Files From Uploading

I strongly believe that the only files that should be deployed are those that are necessary to run the application. Development related files could leak potentially sensitive information or reveal more about the dev environment and technology stack than necessary. Capistrano can be configured to ignore these files by utilizing the .gitattributes file. All you have to do is specify files, paths, or regex along with the export-ignore directive. More information is available via Capistrano’s Documentation.

Adding Custom Tasks

If you need to perform any tasks as part of the deploy process, you can create them as .rake files in lib/capistrano/tasks/*.rake. For example, here’s a task that I use to make sure an application’s .htaccess file has the correct server name for Shibboleth authentication to work since the vhost isn’t the same as the server’s hostname.

namespace :deploy do
  task :correct_htaccess do
    on roles(:web) do |host|
      htaccess_path = [release_path, '.htaccess'].join('/')
      server = ''
      if host.hostname.eql? 'development.example.com'
        server = 'exampledev.com'
      elsif host.hostname.eql? 'production.example.com'
        server = 'example.com'
      end
      execute :sed, '-Ei', "'s/(ShibApplicationId)(.*)$/\\1 #{server}/'", htaccess_path unless server.empty?
    end
  end

  after :cleanup, :correct_htaccess
end

If you find yourself needing to manually modify any files as part of the deploy process, migrating them to tasks is a great way to ensure those processes are documented, under version control, and can be performed automatically as part of an application release.

Capistrano