Deploying WordPress with Capistrano on Godaddy Shared Hosting

Fahrenheit Marketing
Fahrenheit Marketing in Development

Recently a client of ours wanted to keep their site hosted on their existing Godaddy shared hosting account. There are several reasons I dislike shared hosting in general and Godaddy particularly, but I was pretty impressed that I was able to get capistrano working with WordPress on their shared hosting server.

On our own servers, we deploy WordPress via dokku. It is a heroku-style docker container manager. We can just do a git-push to the dokku server to release a new version. It spins up a new container, checks out our repo, and runs all of our build scripts. I will do a blog post soon about this, but there are a few more details I want to improve about it before I publish.

WordPress, the right way

When building WordPress sites, we use a base theme called Roots and the Bedrock WordPress stack. Roots is a great minimal base theme that incorporates Bootstrap, Grunt builds for javascript and less or sass, as well as a more logical template structure. Bedrock goes a bit further and uses Composer to manage plugins and WordPress source code and incorporates the phpdotenv library so that you can keep your production/staging/development configuration details outside of the code and in your environment variables or in dedicated config files. Bedrock also includes basic Capistrano deployment config. The rest of this post will assume that you have set up a WordPress site with Bedrock before. If you have not, there are some great tutorials on the Roots home page.

Getting it set up

In order to make capistrano work with GoDaddy, you have to get a few things set up in the shared hosting environment. Firstly, you need to have ssh access. SSH access can be enabled on any linux hosting account at GoDaddy from the hosting control panel. Once that is enabled, you will probably want to add your public key so that you can connect without a password. There is nothing special about this on GoDaddy. The ssh-copy-id program will do this for you if you don’t want to do it manually.

Since capistrano will use git to checkout your latest code on the server, you need to make sure you can execute git on your GoDaddy hosting account. This is very hit-or-miss. Sometimes your GoDaddy hosting account will not have an executable ssh binary for git to use. As far as I can tell this is arbitrary, depending on which server your account is assigned to. My client had two hosting accounts that were created within a few months of each other. They were otherwise identical but one had ssh marked as non-executable by his account. I did not find a good workaround for this. In my case I just consolidated both sites onto the same account – the one that had an ssh client available.

You will also need to install Composer. Composer’s default installation script may not work for you due to the firewalls. Just download it locally on your workstation and upload the composer.phar file. You’ll need this to be in your path so that Capistrano can find it. I put it in ~/bin and added ~/bin to my path. Capistrano won’t see your .bash_profile, so you will need to make sure that ~/bin is in your path environment variable for the capistrano production deployment. In Bedrock, set this in config/deploy/production.rb:

set :default_env, { path: "/home/content/00/000000/bin:$PATH" }

In addition to composer, I installed Nodejs and Ruby so that I could run the grunt tasks for root on each deploy. This site is using SASS instead of Less so Ruby is required. I was happy to find that nvm installation for Nodejs worked great, as well as rvm for Ruby:

curl https://raw.githubusercontent.com/creationix/nvm/v0.20.0/install.sh | bash
nvm install v0.10.33
curl -sSL https://get.rvm.io | bash -s stable
rvm install 2.1.5

I also added the bin directories for the installed versions of ruby and nodejs to the default_env path in the production.rb file.

One last step on the Godaddy side. Create a deployment directory where capistrano will work:

mkdir -p ~/deploy/tmp
mkdir -p ~/deploy/shared/web/app/uploads
chmod a+xw ~/deploy/shared/web/app/uploads

Capistrano will be pushing your code up into that deploy directory, but it is outside of the web root. We will use a symlink and some ModRewrite trickery to make Godaddy serve your page from the deploy directory.

Make a simlink from the deploy directory to your html web root:

ln -s ../deploy/current/web ~/html/web

Don’t be concerned that the destination of that link doesn’t exist. It will exist after the first deploy.

Now edit the .htaccess file in your document root (~/html/.htaccess).

RewriteEngine on
RewriteRule ^(.*)$ /web/$1

Now it will be as if ~/deploy/current/web is your document root. At this point your site will be broken. If you have a live site running from your account, you may want to wait and do this last.

Configuring Capistrano

Now that the tools are installed on the godaddy side, it is time to get Capistrano set up. We will be editing Gemfile, config/deploy.rb and config/production.rb within the root of your Bedrock project.

source 'https://rubygems.org'

gem 'capistrano', '~> 3.2.0'
gem 'capistrano-composer'
gem 'capistrano-npm'
gem 'capistrano-grunt', github: 'roots/capistrano-grunt'
set :stage, :production
set :tmp_dir, '/home/content/00/0000000/deploy/tmp'
set :deploy_to, -> { "/home/content/00/0000000/deploy" }
set :default_env, { path: "/home/content/00/0000000/bin:/home/content/00/0000000/.nvm/v0.10.33/bin:$PATH" }

# Extended Server Syntax
# ======================
server 'yourserver.com', user: 'username', roles: %w{web app db}

fetch(:default_env).merge!(wp_env: :production)
require 'capistrano/npm'
require 'capistrano/grunt'

set :application, 'yourproject'
set :repo_url, 'ssh://[email protected]:443/yourteam/yourproject.git'

# Branch options
# Prompts for the branch name (defaults to current branch)
#ask :branch, -> { `git rev-parse --abbrev-ref HEAD`.chomp }

# Hardcodes branch to always be master
# This could be overridden in a stage config file
set :branch, :master


set :log_level, :info

# Apache users with .htaccess files:
# it needs to be added to linked_files so it persists across deploys:
set :linked_files, %w{.env web/.htaccess}
set :linked_dirs, %w{web/app/uploads}

set :npm_target_path, -> { release_path.join('web/app/themes/roots') }
set :npm_roles, :app

set :grunt_tasks, 'build'
set :grunt_file, -> { release_path.join('web/app/themes/roots/Gruntfile.js') }
set :grunt_target_path, -> { release_path.join('web/app/themes/roots') }
set :grunt_roles, :app

before 'deploy:updated', 'grunt'

namespace :deploy do
  desc 'Restart application'
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
      # Your restart mechanism here, for example:
      # execute :service, :nginx, :reload
    end
  end
end

# The above restart task is not run by default
# Uncomment the following line to run it on deploys if needed
# after 'deploy:publishing', 'deploy:restart'

Gemfile

Since we will be using grunt during deployment we will also need to add npm and grunt capistrano plugins to our Gemfile.

config/deploy/production.rb

In config/deploy/production.rb we have already set the path so that it could find composer, ruby and nodejs. We will now set the tmp_dir and the deploy_to directory to match the folders we just created. Wel also need to set the server hostname and user account to match our godaddy ssh details.

config/deploy.rb

In config/deploy.rb add the require statements to include the npm and grunt plugins.

The npm plugin will automatically execute on before deploy:updated, so we are just telling it to run within the roots theme folder. For grunt we are telling it to run the build task in the roots theme folder and we also have to tell it to run before deploy:updated. I had to set npm_roles and grunt_roles to the :app role as well, because they attempted to use :all which was not defined in my environment. That may be not be the case in newer Bedrock versions.

Even if the Godaddy account has an SSH client, the default SSH port (22) will be blocked by the firewall. This is not a problem. GitHub and BitBucket both have alternate SSH access points on port 443 (ssh://[email protected]:443/foo/bar.git, or ssh://[email protected]:443/foo/bar.git). Just alter your git URLs to use that alternate port.

The config/deploy.rb has changed significantly in a recent update to Bedrock, but I think these should still work fine. I will update if I try again with a newer version.

Ready, set, deploy!

You will need to create a .env file on the Godaddy server at deploy/shared/.env. It will have the database connection parameters that Godaddy provides when you create a mysql database as well as your WP_HOME and WP_SITEURL settings. Push up your changes, your database and your .env file. Now you should be ready to deploy with capistrano.

Before running capistrano you will need to have Bundler installed and run bundle install in your project root. Then you should be able to deploy with bundle exec cap production deploy.

In my experience, SSH connections to godaddy servers have about a 75% chance of failing. Just retry until the ssh connection succeeds. Once the it starts successfully, the SSH session will stay open reliably.