No More Seat Costs: Semaphore Plans Just Got Better!

    21 May 2015 · Software Engineering

    How to Use Capistrano to Deploy a Rails Application to a Puma Server

    11 min read
    Contents

    Introduction

    Deploying a Ruby on Rails application involves a number of steps, such as copying source code files, running database migrations, pre or post-compiling assets and restarting the web server. Our goal as developer should be to automate this procedure down to a single command we can run at any time.

    There are tools that can help with this. In this tutorial, you will learn how to configure a deployment tool called Capistrano, which essentially can run arbitrary task on a remote machine over SSH.

    Server Requirements

    A typical Rails application server has the following components:

    • 1 GB of RAM or more
    • A modern version of Ruby (2.0+)
    • Bundler and Git installed
    • Apache or Nginx configured as reverse proxy
    • A non-root user with write permissions to your target deploy directory (in the rest of the article we will assume that this user is called “deployer”).

    In this tutorial we will assume that you have such a server already available.

    Installing Capistrano

    Let’s start by installing Capistrano. To do so, add the following lines to your Gemfile and run bundle install:

    gem 'capistrano'
    gem 'capistrano-rails'
    gem 'capistrano3-puma'
    gem 'puma'

    If your server is using RVM, add gem 'capistrano-rvm'. For rbenv users, there is capistrano-rbenv. If you prefer to use another server other than Puma, you should substitute the 'capistrano3-puma' and 'puma' gems with those corresponding to your server of choice. Capistrano’s GitHub profile contains a list of available gems.

    Once Capistrano is installed, run the following command inside your project directory:

    bundle exec cap install
    

    This will generate a few example configuration files.

    You should find the example files generated by Capistrano under config/deploy. Both files (staging.rb and production.rb) have the same contents. It’s a good idea to take a look at them to familiarize yourself with the Capistrano configuration format.

    Here is a listing of the generated files in a tree format:

    |-- Capfile
    |-- config
    |   |-- deploy
    |   |   |-- production.rb
    |   |   `-- staging.rb
    |   `-- deploy.rb
    `-- lib
      `-- capistrano
          `-- tasks
    

    Before we can continue to create our own configuration, we will have to generate an SSH key-pair for passwordless authentication.

    Generating SHH Keys

    SSH allows you to use a pair of cryptographic keys for authentication instead of a password. Programs can use these keys to log into your server to automate tasks. To generate a new pair of keys, run the following command:

    ssh-keygen
    

    The program will ask you for the location where the key pair is going to be saved. The default is fine, unless you already have a pair of keys created. It will also ask for a password – you should leave it blank.

    semaphoreci@ubuntu:~$ ssh-keygen
    Generating public/private rsa key pair.
    Enter file in which to save the key (/home/semaphoreci/.ssh/id_rsa):
    Enter passphrase (empty for no passphrase):
    Enter same passphrase again:
    Your identification has been saved in /home/semaphoreci/.ssh/id_rsa.
    Your public key has been saved in /home/semaphoreci/.ssh/id_rsa.pub.
    The key fingerprint is:
    c4:b8:ec:f5:5d:27:97:f0:5b:46:58:e3:4a:f5:87:64 semaphoreci@ubuntu
    The key's randomart image is:
    +--[ RSA 2048]----+
    |              Eo.|
    |       o     oo+o|
    |      . o    oo.+|
    |     . o    . +.o|
    |      o S    .oo=|
    |     . . . . . =o|
    |      .   . .  . |
    |                 |
    |                 |
    +-----------------+
    

    The key pair is composed of a public key and a private key. You should never share your private key, as that would give access to your server to anyone who has it.

    Before you can actually log in, you will have to install the public key into your app server. To do that, copy the contents of the ~/.ssh/id_rsa.pub (local) file into /home/deployer/.ssh/authorized_keys (on the server). Don’t forget to substitute “deployer” with the actual user on your server.

    Alternatively, you can use the ssh-copy-id script to do this step for you.

    # Only do this if you haven't done the manual step of copying the key
    ssh-copy-id <user>@<your-server-ip>
    

    Once that’s done, you should be able to SSH manually into your server without using a password to verify that everything is working as expected.

    ssh <user>@<your-server-ip>
    Welcome to Ubuntu 14.04.2 LTS (GNU/Linux 3.11.0-20-generic x86_64)
    

    If you would like a more complete explanation on how SSH works, please refer to the SSH getting started guide on Semaphore Community.

    Configuring Capistrano for Deployment

    We are now ready to write our configuration. Open config/deploy.rb in your favorite editor and pay attention to the following lines:

    set :application, 'my_app_name'
    set :repo_url, 'git@example.com:me/my_repo.git'
    set :deploy_to, '/var/www/my_app_name'

    These are pretty self-explanatory – you should modify them to suit your case. For example, if your application name was ‘Shopping site’, the repository URL git@github.com:matugm/shopping-site.git, and you wanted to host the application inside the /var/www/shopping-site directory on your server, the configuration would look like this:

    set :application, 'Shopping site'
    set :repo_url, 'git@github.com:matugm/shopping-site.git'
    set :deploy_to, '/var/www/shopping-site'

    Cloning a Private Repository

    If your repository is private, then you will have to generate another pair of SHH keys, but this time on your server. Make sure you do this as your deploy user.

    If your repository is hosted on GitHub, copy the public SSH key and add it as a deploy key to your project This should enable your server to clone the repository. To verify, SSH as your deploy user to your server and run the git clone once manually:

    deployer@example.com:~$ git clone git@github.com:matugm/shopping-site.git
    

    Configuring Deployment to Production

    Next, you need to edit config/deploy/production.rb. In this file, you tell Capistrano your server’s domain or IP address, the user it should log in as, and the roles of this server. For a small app, one server should be able to manage all three roles. The roles are:

    • app – the Rails application itself
    • db – the database
    • web – the front-end server that will act as a proxy and serve static assets (e.g. nginx)

    For large applications, you may want to consider separating the roles into different servers.

    In our case, you should have the following line:

    server 'example.com', user: 'deploy', roles: %w{app db web}

    Finally, you will need to edit the Capfile file and uncomment the additional tasks you want it to perform during deployment. For example, uncommenting the following lines will make it run bundler, pre-compile assets, run migrations and restart Puma.

    require 'capistrano/bundler'
    require 'capistrano/puma'
    require 'capistrano/rails/assets'
    require 'capistrano/rails/migrations'

    Environment Variables

    Rails 4 requires the secret key setting to be set. In production, it picks this value from the SECRET_KEY_BASE environment variable. You should set this value in the /home/deployer/.bashrc file on your server (preferably at the start of it).

    To generate a secret for the first time, you run be bundle exec rake secret inside your project directory.

    Here is an example of setting your secret key in /home/deployer/.bashrc:

    export SECRET_KEY_BASE=8304hl047oy8743kjhfksdhf9734dsfjosdfu08434

    Puma Setup

    Your puma configuration file will be located on the server under <deploy-directory>/shared/puma.rb. You will need to make some directories for it to store logs and temporary files. You will also need to replace with your actual directory (the same one you specified via :deploy_to in Capistrano configuration). Remember that these files reside only on your server, so you won’t find them in your local project directory.

    mkdir -p <deploy-directory>/shared/tmp/sockets/
    mkdir -p <deploy-directory>/shared/tmp/pids
    mkdir -p <deploy-directory>/shared/log

    Running Your First Deploy

    At this point, we should be able to deploy our app. Since you can have multiple deploy environments, we need to tell Capistrano which one we want to deploy to – in this case production (from config/deploy/production.rb).

    bundle exec cap production deploy

    You will see a lot of output from Capistrano that explains what it is doing. If everything went right, your app should be up and running on the IP address or domain that you specified in production.rb.

    INFO [844e3cd0] Finished in 0.139 seconds with exit status 0 (successful).
    DEBUG [7dae283d] Running /usr/bin/env [ -f /var/www/shopping-site/shared/puma.rb ] as root@46.101.29.162
    DEBUG [7dae283d] Command: [ -f /var/www/shopping-site/shared/puma.rb ]
    DEBUG [7dae283d] Finished in 0.106 seconds with exit status 1 (failed).
    WARN puma.rb NOT FOUND!
    DEBUG Uploading /var/www/shopping-site/shared/puma.rb 0.0%
    INFO Uploading /var/www/shopping-site/shared/puma.rb 100.0%
    INFO puma.rb generated
    DEBUG [48ff8212] Running /usr/bin/env [ -f /var/www/shopping-site/current/REVISION ] as root@46.101.29.162
    DEBUG [48ff8212] Command: [ -f /var/www/shopping-site/current/REVISION ]
    DEBUG [48ff8212] Finished in 0.104 seconds with exit status 1 (failed).
    DEBUG [651332d9] Running /usr/bin/env [ -f /var/www/shopping-site/repo/HEAD ] as root@46.101.29.162
    DEBUG [651332d9] Command: [ -f /var/www/shopping-site/repo/HEAD ]
    DEBUG [651332d9] Finished in 0.110 seconds with exit status 1 (failed).
    DEBUG [299f1ee6] Running /usr/bin/env if test ! -d /var/www/shopping-site; then echo "Directory does not exist '/var/www/shopping-site'" 1>&2; false; fi as root@46.101.29.162
    DEBUG [299f1ee6] Command: if test ! -d /var/www/shopping-site; then echo "Directory does not exist '/var/www/shopping-site'" 1>&2; false; fi
    DEBUG [299f1ee6] Finished in 0.107 seconds with exit status 0 (successful).
    INFO [c9d1bf2c] Running /usr/bin/env git clone --mirror https://github.com/matugm/shopping-site.git /var/www/shopping-site/repo as root@46.101.29.162
    DEBUG [c9d1bf2c] Command: cd /var/www/shopping-site && ( GIT_ASKPASS=/bin/echo GIT_SSH=/tmp/Shopping-site/git-ssh.sh /usr/bin/env git clone --mirror https://github.com/matugm/shopping-site.git /var/www/shopping-site/repo )
    DEBUG [c9d1bf2c]  Cloning into bare repository '/var/www/shopping-site/repo'...
    INFO [c9d1bf2c] Finished in 1.648 seconds with exit status 0 (successful).
    DEBUG [1c3184be] Running /usr/bin/env if test ! -d /var/www/shopping-site/repo; then echo "Directory does not exist '/var/www/shopping-site/repo'" 1>&2; false; fi as root@46.101.29.162
    DEBUG [1c3184be] Command: if test ! -d /var/www/shopping-site/repo; then echo "Directory does not exist '/var/www/shopping-site/repo'" 1>&2; false; fi
    DEBUG [1c3184be] Finished in 0.130 seconds with exit status 0 (successful).
    INFO [b52f660b] Running /usr/bin/env git remote update as root@46.101.29.162
    DEBUG [b52f660b] Command: cd /var/www/shopping-site/repo && ( GIT_ASKPASS=/bin/echo GIT_SSH=/tmp/Shopping-site/git-ssh.sh /usr/bin/env git remote update )
    DEBUG [b52f660b]  Fetching origin
    INFO [b52f660b] Finished in 0.855 seconds with exit status 0 (successful).
    DEBUG [abe79a76] Running /usr/bin/env if test ! -d /var/www/shopping-site/repo; then echo "Directory does not exist '/var/www/shopping-site/repo'" 1>&2; false; fi as root@46.101.29.162
    DEBUG [abe79a76] Command: if test ! -d /var/www/shopping-site/repo; then echo "Directory does not exist '/var/www/shopping-site/repo'" 1>&2; false; fi
    DEBUG [abe79a76] Finished in 0.137 seconds with exit status 0 (successful).
    INFO [4e14fc8b] Running /usr/bin/env mkdir -p /var/www/shopping-site/releases/20150509170957 as root@46.101.29.162
    DEBUG [4e14fc8b] Command: cd /var/www/shopping-site/repo && ( GIT_ASKPASS=/bin/echo GIT_SSH=/tmp/Shopping-site/git-ssh.sh /usr/bin/env mkdir -p /var/www/shopping-site/releases/20150509170957 )
    INFO [4e14fc8b] Finished in 0.141 seconds with exit status 0 (successful).
    INFO [70ad5204] Running /usr/bin/env git archive master | tar -x -f - -C /var/www/shopping-site/releases/20150509170957 as root@46.101.29.162
    DEBUG [70ad5204] Command: cd /var/www/shopping-site/repo && ( GIT_ASKPASS=/bin/echo GIT_SSH=/tmp/Shopping-site/git-ssh.sh /usr/bin/env git archive master | tar -x -f - -C /var/www/shopping-site/releases/20150509170957 )
    

    Conclusion

    In this article we demonstrated how to configure Capistrano to deploy a Rails application to a Puma server. You can find more information about Capistrano and its options on capistranorb.com. Once it’s configured, getting your code online is just one command away. Go ahead and start deploying, but don’t forget to run your tests first!

    P.S. Would you like to learn how to build sustainable Rails apps and ship more often? We’ve recently published an ebook covering just that — “Rails Testing Handbook”. Learn more and download a free copy.

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    Avatar
    Writen by:
    Software developer with a passion for clean and fast code. He loves Ruby and using Linux. Blog author: blackbytes.info.