Deploy WordPress with Capistrano on Bluehost

- 32 comments

Bedrock,Development,Roots

It’s no secret that the Roots theme is a robust starter theme for serious WordPress professionals. It uses Bootstrap and integrates Grunt to enable seasoned WordPress developers to create outstanding sites while using a modern workflow. I’ve been using it as my starter theme for custom WordPress sites for the past three years and I couldn’t be happier.

Roots also has a twelve factor app called Bedrock for taking WordPress development to the next level with Composer for dependency (i.e., plugin) management and the ultimate goal of enabling developers to deploy WordPress with Capistrano rapidly, securely, and with a single command.

I have not had much exposure to Ruby, which powers Capistrano, but the twelve factor app methodology’s organization and single command deployments (and rollbacks) appealed to me, so I wanted to try it out on a small project, namely the revamp of this site.

For now this site is hosted on Bluehost and I’m comfortable in this dev environment, so I wanted to get my feet wet here with Capistrano and not worry about simultaneously jumping into possible server config issues. What better way than to deploy my revamp using Bedrock?

Bedrock is aimed at developers hosting WordPress on VPS servers running Nginx, and since I will be bucking the trend pretty hard by deploying to a shared host running Apache, I thought my first post should chronicle the steps I took for a successful Bedrock deployment.

Before going through these steps you should already:

  • Be comfortable using a command line interface.
  • Be comfortable using Git for version control; you should be able to commit, push, and pull from both your localhost and your shared host without errors. NB, I use Bitbucket for free private Git repos.
  • Read through the Bedrock documentation and install its required applications. Note: Composer is not listed as a Bedrock requirement but I will assume you have it installed on your localhost.

I’m running OS X and my shared host is Bluehost, but Dreamhost, Hostgator, and other shared hosts running cPanel should work with some minor differences; a quick Google search should help you if you’re running Windows or if you get stuck. If you’re on GoDaddy then get off of it. So let’s get Bedrock setup and deployed!

1. SSH Access

You absolutely need SSH access to your shared hosting account. I won’t go over that in this post because it’s been widely addressed. Here are Bluehost’s SSH instructions.

Also, my SSH connection uses authorized keys without prompting me for a password so that is how I went through this process, I recommend you setup authorized keys on Bluehost too.

2. Get Bedrock

Create a new project directory on your localhost and clone Bedrock:

$ git clone https://github.com/roots/bedrock.git .

3. Install Dependencies

Bedrock manages its dependencies with Composer. WordPress itself is even one of Bedrock’s dependencies, so to install WordPress and its other dependencies run:

$ composer install

4. Set Environmental Variables

One of the big goals Bedrock has is to separate configuration files from your WordPress sites, particularly when they contain sensitive information (i.e., login credentials and passwords). It keeps these tucked away in a .env file, which we can create by using the example it ships with:

$ cp .env.example .env

This file should be self explanatory if you’re relatively experienced with WordPress. Make sure you enter your localhost credentials here. Once that is setup you should be able to access your site from the URL you entered in .env.

If you’re lost, check out the Bedrock quick start documentation.

5. Add a Theme and Get to Work!

Now you can add your theme, create a Git repository, and make some commits which we will deploy in the next steps.

6a. Setup Your Subdomain or Addon Domain on Bluehost

Setup your folder structure as you would for creating a new project: either create a new subdomain or addon domain.

Ensure sure your config is set to use PHP 5.4, not PHP 5.4 (Single php.ini) and not PHP 5.4 (FastCGI).

Then create your database, add a user to the database, and import your database content from your localhost (if necessary). Remember to change your URLs and account for serialization (i.e., don’t just search and replace, I personally like Peach).

While we’re in phpMyAdmin (on Bluehost), you will need to change your wp_options.siteurl entry to http://dev.example.com/current/web/wp.

6b. Install Composer on Bluehost

I found the best place to install Composer is in your home directory (~), and was able to do it quickly1:

$ curl -sS https://getcomposer.org/installer | php

The -sS switches just prevent curl from displaying its download progress but enable the display of error messages on failure.

Once that’s done you should be able to verify Composer was installed by running:

$ php-cli composer.phar

And while we’re here:

  • cd into your home directory (~) and do a quick $ pwd–save the result somewhere handy
  • type $ which bash–save the result somewhere handy

7. Create a Temporary Directory for Capistrano

During my deploy, Capistrano tried to access the /tmp/ directory and failed because it did not have permission. So, while in my Bluehost shell I did a quick:

$ mkdir ~/capistrano_tmp

In step 8 we will configure Capistrano to use this temporary directory instead of /tmp/

8. Setup deploy.rb

Back on your localhost, the Bedrock directory should have a file we need to edit: config/deploy.rb.

Line 1: the application name should be the directory (relative to ~/public_html/) where you setup your subdomain or add-on domain.

Line 2: change the URL to your Bitbucket repo URL. Mine looked like git@bitbucket.org:my_username/my_repo.git.

Line 12: change /srv/www/ to your server directory–the one we found at the end of step 6b–and add a trailing /public_html/. So if step 6b revealed your server directory to be /home2/whatever, your new line 15 should read:

set :deploy_to, , -> { "/home2/whatever/public_html/#{fetch(:application)}" }

Line 14: change :info to :debug in case your deployment fails and you need help figuring out where it went wrong.

Line 16: change this line to read set :linked_files, %w{.env web/.htaccess}2.

Line 18: should be empty but we need to tell Capistrano to use the temporary directory we created for it in step 7 using the server path we saved from step 6b. Add this line:

set :tmp_dir, "/home2/whatever/capistrano_tmp"

Now let’s grab the result from $ which bash and $ pwd that we saved from step 6b and add these lines at the end of the file:

SSHKit.config.command_map[:bash] = "%YOUR_BASH_PATH%"

SSHKit.config.command_map[:composer] = "php-cli %YOUR_HOME_PATH%/composer.phar"

Here is my finished file:

I rearranged the last two lines but you can order those any way you like.

9. Setup staging.rb

On your localhost, open up config/deploy/staging.rb. All we need to change here is line 11 by adding our server URL and our username. These should be the same credentials that you use when connecting to Bluehost via SSH.

10. Deployment Check

Let’s have Capistrano prepare for our deploy and create its folder structure and symlinks by running (on your localhost):

$ bundle exec cap staging deploy:check

I’m assuming you’ve called the stage we’re deploying to staging, but you can call it anything.

11a. Final Prep

Assuming your deploy:check went well, let’s copy our local .env to our remote project’s shared/ directory.

While we’re in shared/, create a directory called shared/web/ and copy the .htaccess file you want Bluehost to use into shared/web/. I used the one from my localhost and it works fine for now.

Now edit your remote shared/.env file and add the Bluehost database credentials for your remote WordPress installation.

Finally, change line 6 to reflect the name of your stage; mine is called staging, so my line reads:

WP_ENV=staging

11b. Setup mod_rewrite

You won’t be able to view your site after it’s deployed unless you add some mod_rewrite rules. If your remote project directory is ~/public_html/dev.example.com/, create a new .htaccess file there:

Finally we can…

Deploy WordPress with Capistrano

Whew, seems like we’ve been through a lot together and now we’re finally ready to deploy! So push all of your commits and run $ bundle exec cap staging deploy and let ‘er rip! If you followed these instructions then you should be well on your way to one line deploys with WordPress + Bedrock + Capistrano + Bluehost! If you experience any issues or have any questions then leave a comment and I will reply.

Thank You

A huge thanks goes out to the Roots team and Bedrock team. They’ve created and maintained exceptional code and helped me through this process. Thank you!

 


  1. http://penawebservices.wordpress.com/2014/01/14/installing-composer-on-bluehost-com/ 
  2. “If you use Apache for your web server… by default Bedrock ignores the .htaccess file both in the version control AND in the deploy configurations. What this means is that not only is your local .htaccess not sent to the server during a deploy, but even if you manually put one on your server it will be deleted each time you deploy. That’s a big problem for using WordPress. To fix this, add the web/.htaccess to the linked files setting in deploy.rb, and then put an .htaccess file in the shared/web folder on the server (you’ll have to create the web/ folder). Your production .htaccess file shouldn’t change very often and is likely to deviate from your development .htaccess file, so keeping it out of the normal workflow and version control is the way to go.”
    http://efeqdev.com/tutorial/using-bedrock-for-wordpress-for-the-first-time/ 

Comments

  1. Thanks for the quite indepth tutorial!

    I have a question about Step 8, Setup deploy.rb

    How would you handle this if you wanted different domains/subdomains for staging and production deployments.

    I’m working on Siteground and have my directories setup like

    public_html/example.com/
    public_html/staging.example.com/

    It seems that the deploy.rb script wants you to specify one application name that is used to set the deploy_to path.

    Am I missing something here?

  2. Hello, Michael.

    The deploy worked fine for my WP site. All files are located where they should be. However, the site won’t load, I just get a blank screen when I try to load it. Have you experienced something like this before? I think maybe it is something to do with my wp-config.php file.

    Thanks for your help.

    1. Hi Guilherme, did you check your server logs for errors? Also make sure your files and dirs have proper permissions (644 and 755, respectively).

      1. Hi Michael,
        first of all, thanks for your article that helped me understand bedrock a few months ago!
        After many installs, I realize that my folders and files don’t have the right permissions (775/664) after deployments.
        How do you manage the change to 755/644 by your side?
        Thanks again,
        Frédéric

        1. Hi Fred,

          Run these on your remote in your WordPress root dir:

          $ find . -type d -exec chmod 755 {} \;
          $ find . -type f -exec chmod 644 {} \;

          Source

  3. Sorry for my long post above, actually you can delete it now. I now know I haven’t done the SSH thing properly.
    What I have done about it so far is,
    1. Enable SSH Access on Bluehost as in here
    2.Create Public and private keys and authorize it as in
    Since I am running windows I installed Putty & Puttygen and successfully establised a SSH access to my bluehost server using the private key I downloaded from bluehost.

    But I wonder how the bundle exec cap can communicate to the bluehost server, because I don’t see a place inside the 11 steps above that does something about enabling my capistrano to get SSH access to my bluehost server.

    I also wonder how it can get access to my bitbucket repo as I haven’t done anything to grant it access. Please give me light on this.

    I have seen the documentation at but couldn’t apply it directly to my context(Running windows).

    Please Help. Thank you!

  4. how do I know if the command has successfully executed or not. Mine returned with this.
    Please let me know if this is what I should expect, BTW It didn’t do anything on my bluehost.

    03/16/2015 03:39 PM 518 .gitignore
    03/16/2015 03:39 PM 221 .travis.yml
    03/16/2015 03:39 PM 380 Capfile
    03/16/2015 03:39 PM 1,799 CHANGELOG.md
    03/16/2015 03:39 PM 1,439 composer.json
    03/16/2015 03:39 PM 8,043 composer.lock
    03/16/2015 05:18 PM config
    03/16/2015 03:39 PM 3,978 CONTRIBUTING.md
    03/16/2015 03:39 PM 86 Gemfile
    03/17/2015 09:26 AM 485 Gemfile.lock
    03/17/2015 10:21 AM 0 hta.txt
    03/16/2015 03:39 PM 1,044 LICENSE.md
    03/16/2015 03:39 PM 17,544 README.md
    03/16/2015 03:39 PM 1,044 ruleset.xml
    03/16/2015 05:18 PM scripts
    03/16/2015 05:39 PM vendor
    03/16/2015 05:18 PM web
    03/16/2015 03:39 PM 13 wp-cli.yml
    16 File(s) 37,586 bytes
    6 Dir(s) 6,458,183,680 bytes free

    C:wampwwwpamojawp>help
    For more information on a specific command, type HELP command-name
    ASSOC Displays or modifies file extension associations.
    ATTRIB Displays or changes file attributes.
    BREAK Sets or clears extended CTRL+C checking.
    BCDEDIT Sets properties in boot database to control boot loading.
    CACLS Displays or modifies access control lists (ACLs) of files.
    CALL Calls one batch program from another.
    CD Displays the name of or changes the current directory.
    CHCP Displays or sets the active code page number.
    CHDIR Displays the name of or changes the current directory.
    CHKDSK Checks a disk and displays a status report.
    CHKNTFS Displays or modifies the checking of disk at boot time.
    CLS Clears the screen.
    CMD Starts a new instance of the Windows command interpreter.
    COLOR Sets the default console foreground and background colors.
    COMP Compares the contents of two files or sets of files.
    COMPACT Displays or alters the compression of files on NTFS partitions.
    CONVERT Converts FAT volumes to NTFS. You cannot convert the
    current drive.
    COPY Copies one or more files to another location.
    DATE Displays or sets the date.
    DEL Deletes one or more files.
    DIR Displays a list of files and subdirectories in a directory.
    DISKCOMP Compares the contents of two floppy disks.
    DISKCOPY Copies the contents of one floppy disk to another.
    DISKPART Displays or configures Disk Partition properties.
    DOSKEY Edits command lines, recalls Windows commands, and
    creates macros.
    DRIVERQUERY Displays current device driver status and properties.
    ECHO Displays messages, or turns command echoing on or off.
    ENDLOCAL Ends localization of environment changes in a batch file.
    ERASE Deletes one or more files.
    EXIT Quits the CMD.EXE program (command interpreter).
    FC Compares two files or sets of files, and displays the
    differences between them.
    FIND Searches for a text string in a file or files.
    FINDSTR Searches for strings in files.
    FOR Runs a specified command for each file in a set of files.
    FORMAT Formats a disk for use with Windows.
    FSUTIL Displays or configures the file system properties.
    FTYPE Displays or modifies file types used in file extension
    associations.
    GOTO Directs the Windows command interpreter to a labeled line in
    a batch program.
    GPRESULT Displays Group Policy information for machine or user.
    GRAFTABL Enables Windows to display an extended character set in
    graphics mode.
    HELP Provides Help information for Windows commands.
    ICACLS Display, modify, backup, or restore ACLs for files and
    directories.
    IF Performs conditional processing in batch programs.
    LABEL Creates, changes, or deletes the volume label of a disk.
    MD Creates a directory.
    MKDIR Creates a directory.
    MKLINK Creates Symbolic Links and Hard Links
    MODE Configures a system device.
    MORE Displays output one screen at a time.
    MOVE Moves one or more files from one directory to another
    directory.
    OPENFILES Displays files opened by remote users for a file share.
    PATH Displays or sets a search path for executable files.
    PAUSE Suspends processing of a batch file and displays a message.
    POPD Restores the previous value of the current directory saved by
    PUSHD.
    PRINT Prints a text file.
    PROMPT Changes the Windows command prompt.
    PUSHD Saves the current directory then changes it.
    RD Removes a directory.
    RECOVER Recovers readable information from a bad or defective disk.
    REM Records comments (remarks) in batch files or CONFIG.SYS.
    REN Renames a file or files.
    RENAME Renames a file or files.
    REPLACE Replaces files.
    RMDIR Removes a directory.
    ROBOCOPY Advanced utility to copy files and directory trees
    SET Displays, sets, or removes Windows environment variables.
    SETLOCAL Begins localization of environment changes in a batch file.
    SC Displays or configures services (background processes).
    SCHTASKS Schedules commands and programs to run on a computer.
    SHIFT Shifts the position of replaceable parameters in batch files.
    SHUTDOWN Allows proper local or remote shutdown of machine.
    SORT Sorts input.
    START Starts a separate window to run a specified program or command.
    SUBST Associates a path with a drive letter.
    SYSTEMINFO Displays machine specific properties and configuration.
    TASKLIST Displays all currently running tasks including services.
    TASKKILL Kill or stop a running process or application.
    TIME Displays or sets the system time.
    TITLE Sets the window title for a CMD.EXE session.
    TREE Graphically displays the directory structure of a drive or
    path.
    TYPE Displays the contents of a text file.
    VER Displays the Windows version.
    VERIFY Tells Windows whether to verify that your files are written
    correctly to a disk.
    VOL Displays a disk volume label and serial number.
    XCOPY Copies files and directory trees.
    WMIC Displays WMI information inside interactive command shell.

    For more information on tools see the command-line reference in the online help.

    C:wampwwwpamojawp>rename?
    'rename?' is not recognized as an internal or external command,
    operable program or batch file.

    C:wampwwwpamojawp>rename?/
    'rename?' is not recognized as an internal or external command,
    operable program or batch file.

    C:wampwwwpamojawp>rename/?
    Renames a file or files.

    RENAME [drive:][path]filename1 filename2.
    REN [drive:][path]filename1 filename2.

    Note that you cannot specify a new drive or path for your destination file.

    C:wampwwwpamojawp>rename hta.txt .htaccess

    C:wampwwwpamojawp>git status

    On branch master

    Your branch is ahead of 'origin/master' by 2 commits.

    (use "git push" to publish your local commits)

    #

    Changes not staged for commit:

    (use "git add ..." to update what will be committed)

    (use "git checkout -- ..." to discard changes in working directory)

    #

    modified: config/deploy.rb

    #

    Untracked files:

    (use "git add ..." to include in what will be committed)

    #

    .htaccess

    no changes added to commit (use "git add" and/or "git commit -a")

    C:wampwwwpamojawp>git add --all
    warning: LF will be replaced by CRLF in config/deploy.rb.
    The file will have its original line endings in your working directory.

    C:wampwwwpamojawp>git commit -m "add .htaccess"
    [master warning: LF will be replaced by CRLF in config/deploy.rb.
    The file will have its original line endings in your working directory.
    a6f89e3] add .htaccess
    warning: LF will be replaced by CRLF in config/deploy.rb.
    The file will have its original line endings in your working directory.
    2 files changed, 11 insertions(+), 1 deletion(-)
    create mode 100644 .htaccess

    C:wampwwwpamojawp>git push
    Password for 'https://robicodes@bitbucket.org':
    Counting objects: 20, done.
    Delta compression using up to 2 threads.
    Compressing objects: 100% (14/14), done.
    Writing objects: 100% (14/14), 1.61 KiB | 0 bytes/s, done.
    Total 14 (delta 8), reused 0 (delta 0)
    To https://robicodes@bitbucket.org/robicodes/pamojafricawp.git
    4ab53e0..a6f89e3 master -> master

    C:wampwwwpamojawp>git status

    On branch master

    nothing to commit, working directory clean

    C:wampwwwpamojawp>git status

    On branch master

    nothing to commit, working directory clean

    C:wampwwwpamojawp>bundle exec cap staging deploy
    C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/pageant.rb:1:in
    require': cannot load
    such file -- dl/import (LoadError)
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/pageant.rb:1:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/agent/socket.rb:5:in requ
    ire'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/agent/socket.rb:5:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/agent.rb:22:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/agent.rb:22:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/key_manager.rb:4:in requi
    re'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/key_manager.rb:4:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/session.rb:4:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/session.rb:4:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh.rb:11:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh.rb:11:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit/backends/netssh.rb:1:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit/backends/netssh.rb:1:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit/all.rb:32:in require_relative'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit/all.rb:32:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit.rb:35:in require_relative'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit.rb:35:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/capistrano-3.2.1/lib/capistrano/all.rb:2:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/capistrano-3.2.1/lib/capistrano/all.rb:2:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/capistrano-3.2.1/bin/cap:2:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/capistrano-3.2.1/bin/cap:2:in
    '
    from C:/Ruby22/bin/cap:23:in load'
    from C:/Ruby22/bin/cap:23:in
    '

    C:wampwwwpamojawp>git status

    On branch master

    nothing to commit, working directory clean

    C:wampwwwpamojawp>git status

    On branch master

    nothing to commit, working directory clean

    C:wampwwwpamojawp>bundle exec cap staging deploy
    C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/pageant.rb:1:in require': cannot load
    such file -- dl/import (LoadError)
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/pageant.rb:1:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/agent/socket.rb:5:in requ
    ire'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/agent/socket.rb:5:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/agent.rb:22:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/agent.rb:22:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/key_manager.rb:4:in requi
    re'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/key_manager.rb:4:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/session.rb:4:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/session.rb:4:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh.rb:11:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh.rb:11:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit/backends/netssh.rb:1:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit/backends/netssh.rb:1:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit/all.rb:32:in require_relative'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit/all.rb:32:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit.rb:35:in require_relative'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit.rb:35:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/capistrano-3.2.1/lib/capistrano/all.rb:2:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/capistrano-3.2.1/lib/capistrano/all.rb:2:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/capistrano-3.2.1/bin/cap:2:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/capistrano-3.2.1/bin/cap:2:in
    '
    from C:/Ruby22/bin/cap:23:in load'
    from C:/Ruby22/bin/cap:23:in
    '

    C:wampwwwpamojawp>bundle exec cap staging deploy
    C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/pageant.rb:1:in require': cannot load
    such file -- dl/import (LoadError)
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/pageant.rb:1:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/agent/socket.rb:5:in requ
    ire'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/agent/socket.rb:5:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/agent.rb:22:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/agent.rb:22:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/key_manager.rb:4:in requi
    re'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/key_manager.rb:4:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/session.rb:4:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh/authentication/session.rb:4:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh.rb:11:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/net-ssh-2.9.1/lib/net/ssh.rb:11:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit/backends/netssh.rb:1:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit/backends/netssh.rb:1:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit/all.rb:32:in require_relative'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit/all.rb:32:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit.rb:35:in require_relative'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/sshkit-1.5.1/lib/sshkit.rb:35:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/capistrano-3.2.1/lib/capistrano/all.rb:2:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/capistrano-3.2.1/lib/capistrano/all.rb:2:in
    '
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/capistrano-3.2.1/bin/cap:2:in require'
    from C:/Ruby22/lib/ruby/gems/2.2.0/gems/capistrano-3.2.1/bin/cap:2:in
    '
    from C:/Ruby22/bin/cap:23:in load'
    from C:/Ruby22/bin/cap:23:in
    '

  5. Thanks for your wonderful post, I have been doing my wordpress deployments over FTP, which almost made me bored. Thanks to the bedrock stack it seems I have the chance to do it professionally. I have followed ur steps and made it as far as 11a, then I paused. Where is the the shared/ thing on my bluehost really? Is it public_html/mysite ???

  6. EDIT: sorry my first comment had the first rewrite rule changed.
    …I was having exactly the same problem. What I did was modify this line on the WordPress generated code in the .htaccess:

    RewriteRule . /index.php [L]

    to this:

    RewriteRule . /current/web/index.php [L]

    and then set the permission to the .htaccess file to 444 so WordPress cannot override it later. Hope this helps.

  7. I seem to have everything working for my deploy except when I run it I get the following failures:

    DEBUG[8b43c06f] Command: [ -L /home/mysite/public_html/staging.mysite.com/releases/20150304201841/.env ]
    DEBUG[8b43c06f] Finished in 0.173 seconds with exit status 1 (failed).
    DEBUG[7f8dbc67] Running /usr/bin/env [ -f /home/mysite/public_html/staging.mysite.com/releases/20150304201841/.env ] on staging.mysite.com
    DEBUG[7f8dbc67] Command: [ -f /home/mysite/public_html/staging.mysite.com/releases/20150304201841/.env ]
    DEBUG[7f8dbc67] Finished in 0.162 seconds with exit status 1 (failed).
    

    and

    DEBUG[0978bdaf] Running /usr/bin/env [ -L /home/mysite/public_html/staging.mysite.com/releases/20150304201841/web/.htaccess ] on staging.heyyyyyy.com
    DEBUG[0978bdaf] Command: [ -L /home/mysite/public_html/staging.mysite.com/releases/20150304201841/web/.htaccess ]
    DEBUG[0978bdaf] Finished in 0.175 seconds with exit status 1 (failed).
    DEBUG[4d6962ab] Running /usr/bin/env [ -f /home/mysite/public_html/staging.mysite.com/releases/20150304201841/web/.htaccess ] on staging.mysite.com
    DEBUG[4d6962ab] Command: [ -f /home/mysite/public_html/staging.mysite.com/releases/20150304201841/web/.htaccess ]
    DEBUG[4d6962ab] Finished in 0.160 seconds with exit status 1 (failed).
    

    and

    DEBUG[9ec03a9b] Running /usr/bin/env [ -L /home/mysite/public_html/staging.mysite.com/releases/20150304201841/web/app/uploads ] on staging.heyyyyyy.com
    DEBUG[9ec03a9b] Command: [ -L /home/mysite/public_html/staging.mysite.com/releases/20150304201841/web/app/uploads ]
    DEBUG[9ec03a9b] Finished in 0.165 seconds with exit status 1 (failed).
    

    Everything else is successful. Anyone have any idea what is causing this and how to fix? It’s tough to be so close to the finish line on this.

  8. So… I’ve got it pushing to my host following the instructions here, but when I try and push another update using cap deploy staging it errors out here:

    Tasks: TOP => deploy:check:make_linked_dirs

    Here’s the line for my linked directories on staging.rb:

    set :linked_dirs, %w(‘web/app/uploads’)

    I want to get this working in a test app before I start using it for actual applications.

  9. Hello, mate!

    Very good tutorial — got my deploys working to a shared server with A Small Orange, but I have encountered one problem upon further investigation: I can’t choose nicely structured Permalinks.

    For example: http://mysite.ca/?p=1 works, but http://mysite.ca/hello-world doesn’t.

    I’ve been making little tweaks here and there to the .htaccess file as well as tried a bunch of different URL combinations, but haven’t gotten anything to show up with any other than the default Permalinks.

    Moreover, I’ve noticed that this site has pretty links, so I was wondering if there was an extra step you took in your .htaccess file (or some code-level change it would be possible to make) that enabled you to do that.

    If not, I’m kind of stumped so I suppose I’ll just keep the ugly links.

    1. Hi Paul, thanks for the kind words and I’m glad you found this useful enough to get yourself up and running with Capistrano. Unfortunately for you I never encountered an issue like that with my site’s permalinks. I highly suggest that you post your issue with more details over on the WordPress Stack Exchange. Be sure to describe your hosting environment in more detail and even paste the contents of your .htaccess. Please also reply here with the link to your question so that other readers and I can have a look and hopefully someone among us can help you sort out your permalinks. Thanks!

    2. …I was having exactly the same problem. What I did was modify this line on the WordPress generated code in the .htaccess:

      RewriteRule . /current/web/index.php [L]

      to this:

      RewriteRule . /current/web/index.php [L]

      and then set the permission to the .htaccess file to 444 so WordPress cannot override it later. Hope this helps.

    3. EDIT: sorry my first comment had the first rewrite rule changed. Also I forgot to hit reply. Damn i’m slow today.
      …I was having exactly the same problem. What I did was modify this line on the WordPress generated code in the .htaccess:

      RewriteRule . /index.php [L]

      to this:

      RewriteRule . /current/web/index.php [L]

      and then set the permission to the .htaccess file to 444 so WordPress cannot override it later. Hope this helps.

  10. Thanks for the tutorial! It’s taken me much closer to successfully deploying my site.

    Unfortunately, I’ve been hung up on a single error for a while now, and can’t seem to figure it out. I’m a beginner with capistrano and google searches don’t turn much up for this error. In step 10, when I run the deploy:check command, capistrano returns the following error:

    ERRORlinked file home//public_html/staging/shared/.env does not exist on 
    cap aborted!
    

    In the tutorial, you create the .env file in step 11, after you run deploy:check, so it’s confusing to me that I would have this problem at all. In any case, this error prompted me to create the shared/ directory and the .env file, which I assumed would solve the problem—alas, capistrano still complains that .env does not exist, even though I’m sure it does.

    Any help on this issue would be much appreciated.

    1. Funny – fixed this issue moments after I posted to your blog. You can delete both of these comments now (it was a stupid error on my part, no point in leaving the comments).

      1. I’d like to leave your comments up in case anyone else comes across a similar issue. You might consider posting the solution that worked for you so others can bask in the Capistrano light! 😉

        1. Fair enough. The issue was that I didn’t have a forward slash at the beginning of my deploy_to line (in front of the home directory). In other words, I had…

          set :deploy_to, -> { "home//#{fetch(:application)}" }

          instead of

          set :deploy_to, -> { "/home//#{fetch(:application)}" }

          A beginner’s mistake, I admit. But it was particularly hard to track down, because all Capistrano could tell me was that the .env file didn’t exist.

  11. Nice post! I had not heard of Peach before, I’m sure that will come in handy. FYI, wp-cli also handles serialized data when doing search-replace on the DB and I’m in the process of editing my Capfile to make use of it. I’ve had success doing Cap deploys on Bluehost, Site5, Dreamhost and Media Temple. I’m about to check out Siteground and Flywheel which seem like new strong hosting contenders.

    If you’re interested, I have a slightly different take on deploying with Capistrano that I wrote about here: http://karveldigital.com/how-to-deploy-wordpress-with-capistrano

    Sadly, I have not yet jumped on the Grunt or Gulp train, but it’s on my list!

    1. Thanks Kronda, I actually read your article and watched the screencast a while back and it’s definitely a must read! My deploy is pretty specific to Bedrock because I’m a diehard Roots developer.

      Roots also introduced me to Grunt which quickly became indispensable in my workflow. If you have some time to check it out the documentation makes it quite easy to dive right in. You’ll be building minified JavaScript and CSS so fast your head will spin and you’ll wonder how you ever developed without it!

  12. I’ve had success on some shared hosts changing the public_html folder to a symlink, that links to the current/app (or current/web) capistrano folder, contained in a another directory above public_html, on the account.

    However, this doesn’t always work–it depends on the host (worked on Host Gator, but not on Media Temple grid, for example). And you have to watch folder permissions. For getting this to work on Host Gator, I had to remove group write permissions from the deployment folders as one of my deployment tasks (this was to undo the permissions that capistrano added automatically, which violated the server configuration and broke the site).

    1. Hi Grant,

      I’ve just tried this as well (Godaddy, ho-hum). With a symlink to ~/public_html/deploy_folder/current/web/ as ~/public_html/a_subdomain, which as the name implies is the root for a subdomain.

      Oddly the page loads, but the theme styling got lost completely, even though I can access all the files via the symlink in the shell and they seem to be accessible through a web-browser.

      The internal wordpress themes work … the wordpress mod_rewrite rules seem to work as well …

      Is my .env files setup incorrectly now? I’ve changed the paths to subdomain.domain.com and subdomain.domain.com/wp.

      It worked previously when just had everything sit in the root of a subdomain folder in public_html, but the unnecessary links (subdomain.domain.com/current/web/) annoyed me and I’m terrible at .htaccess files and mod_rewrite.

      Anyone, any ideas? I’m at my wit’s end.

      Thanks!
      mike

      1. Hi everybody,

        I found my mistake. It was a trailing slash in the .env files that screwed up the paths for my roots-theme styling (ah, what a beginner’s mistake).

        But I can confirm that having a subdomain point directly to the symlink web folder works well, so does a symlink folder pointing to the web folder.

        And it works on cheap goDaddy Linux hosting, though you are limited to only one SSH account. At least I couldn’t add any users via the command line, as the Capistrano guides recommend doing.

        Great guide, Michael! Definitely saved me a lot of sweat and tears.

        I’m a bit disappointed that the roots people put up only a half-hearted documentation on the whole bedrock thing and then put the screencast for capistrano behind a paywall.

        Pip-pip,
        Mike

  13. Wondering if it is possible to deploy production to the www/bare domain instead of a subdomain.
    IOW: cap deploy the production (live) version to the root (public_html) because that is where cpanel automatically accesses the domain’s doc root.

    Thanks for writing this up! Great reference!

    1. I did that for this site in a way, but sometimes I need to host staging subdomains on this account and I didn’t want this site’s files and directory structure cluttering my ~/public_html/ directory so it’s actually deployed to ~/public_html/cfxdesign.com/. It’s definitely doable, it just takes a bit of .htaccess trickery. Instead of putting the .htaccess with mod_rewrite into shared/web/ I stuck it in my ~/public_html/ directory and edited it accordingly. shared/web/ just contains a default WordPress .htaccess. The only caveat is that my ~/public_html/.htaccess needs to have AddHandler application/x-httpd-php54 .php at the top to ensure the correct version of PHP, along with Bluehost’s php.ini.

      1. Thanks Michael. (And Grant for the symlink idea).

        Have either of you used this env to update the databases and sync uploads folder on staging and local dev systems? Essentially pulling the current database/uploads dir to the dev copies in order to have a duplicate of the system as published?

        1. I’m taking steps in that direction because I’ve already been doing it manually. That workflow isn’t suited to every project (think ecommerce where potentially sensitive custom info might be stored in the db) but for smaller informational apps I don’t see any huge issues. I’ve had great success using grunt-exec and grunt-peach separately. When I get them working nicely together then I will definitely be writing a post on it… which should be soon.

Leave a Comment