Deploying a Rails app to Digital Ocean with Capistrano
Tags: rails, production, deploy, beginners
Published: 2018-10-04 15:21:18

---
title: Deploying a Rails app to Digital Ocean with Capistrano
published: true
description: Basic server set up to to deploy your application with capistrano
tags: ruby on rails, production, deploy, beginners
---

This is the basic steps on how I setup my DO servers and deploy my rails apps. I originally learned this from the GoRails screencast but have been iterating over it and tweaking/improving it over time. Maybe this write up will help you get your apps live or maybe you have a better way you can share with me.

If you don't have a Digital Ocean account, you can sign up using this link, [https://m.do.co/c/33c1f4fb065a](https://m.do.co/c/33c1f4fb065a). Yes I get a referral credit if you use my link but only if you end up spending money with them, even if you dont you still get $100 in credits for 60 days if you sign up during Hacktoberfest.

Lets spin up a server!

### Creating a droplet

* Create a droplet
  * Choose Ubuntu as your distribution (16.04.4 x64)
  * Choose the size of your droplet (min 1GB and 1CPU)
  * Choose New York 1 or 3 for your datacenter (I'm on the East Coast so this makes the most sense for me)
  * Optional:
    * Add SSH keys now (can also do this later)
    * Rename the hostname (generally to something that makes sense)

* Once the droplet is created an email is sent with the login credentials
* Use the provided credentials to SSH into the new droplet
* It is best practice to make a new user to manage deployments rather that using `root`
  * `$ sudo adduser deploy`
  * `$ sudo adduser deploy sudo`
  * `$ su deploy`

If you did not provide SSH keys when you created the droplet you should do that now.  If you did, skip this:

#### Do this on your computer NOT on the server!

I use a MacOS tool called ssh-copy-id. If you don't have it you can get it by running `$ brew install ssh-copy-id`
    * Run `$ ssh-copy-id deploy@SERVERIP` where SERVERIP is the IP address of your droplet

Back to our regularly scheduled program

* SSH back into the server as the deploy user

Run the following on the server as deploy

* First get Node and Yarn.
  * `$ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -`
  * `$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -`
  * `$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list`

* Then install you basic dependencies.
  * `$ sudo apt-get update`
  * `$ sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev software-properties-common libffi-dev nodejs yarn`

* Then install rbenv (for managing our ruby versions, you can use rvm if you want but I'm partial to rbenv)
  * `$ cd`
  * `$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv`
  * `$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc`
  * `$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc`
  * `$ exec $SHELL`

  * `$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build`
  * `$ echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc`
  * `$ exec $SHELL`

* Finally install ruby
  * `$ rbenv install 2.4.1` -- or whatever version of ruby your application is running
  * `$ rbenv global 2.4.1` -- or whatever you just installed
  * `$ ruby -v` -- to verify the correct version is installed and set

  * `$ gem install bundler`
  * `$ rbenv rehash`

#### Setup Nginx with Passenger  
This will actually run our 'server' and serve (no pun intended) our app

* `$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7`
* `$ sudo apt-get install -y apt-transport-https ca-certificates`

* `$ sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger xenial main > /etc/apt/sources.list.d/passenger.list'`
* `$ sudo apt-get update`
* `$ sudo apt-get install -y nginx-extras passenger`

##### Check to make sure everything you just did worked correctly

Run `$ sudo service nginx start` on the server and then visit the droplets IP address in the browser

If you did everything right so far you should see an Nginx Welcome message.

##### Configure Nginx and Passenger

* `$ sudo vim /etc/nginx/nginx.conf`
  * Find the following lines of code and uncomment `include /etc/nginx/passenger.conf`:
  <pre>
   ##
   # Phusion Passenger
   ##
   # Uncomment it if you installed ruby-passenger or ruby-passenger-enterprise
   ##

   include /etc/nginx/passenger.conf;
  </pre>
  * Save and close (:wq)

* `$ sudo vim /etc/nginx/passenger.conf`
  * Change the passenger_ruby line to point to rbenv (or rvm if you used that):
    * `passenger_ruby /home/deploy/.rbenv/shims/ruby;`
  * Save and close (:wq)

##### Once this is all completed restart the Nginx Server
* `$ sudo service nginx restart`

### Environmental Variables

* Ensure you are in the top most level of the deploy user section
  * Run `$ cd ~`
* `$ git clone https://github.com/rbenv/rbenv-vars.git $(rbenv root)/plugins/rbenv-vars`
* `$ vim .rbenv-vars`
* Add any Environmental Variables you need to the file.  

### PostgreSQL

* Run `$ sudo apt-get install postgresql postgresql-contrib libpq-dev`
* Set up the postgres user (also named "deploy" but different from our linux user named "deploy") and DB
  * `$ sudo su - postgres`
  * `$ createuser --pwprompt deploy` -- make note of the password you choose
  * `$ createdb -O deploy APP_NAME_production` -- where APP_NAME is the name of the application
  * `$ exit`


### Deploying

First we need to configure our repo's bucket to be able to talk to our server.  Since we deploy directly from the repo rather that our local machine.
  * While SSHed in our server, enter `ssh-keygen` at the command line.
  * Follow the prompts to set up a new key. A passphase is not needed in this case.
  * Once the server has generated a new key run `cat ~/.ssh/id_rsa.pub` and copy the returned key
  * Add the key to the App's repo
    * If you're using bitbucket that can be found in *settings* > *access keys*

Back on your local machine let's set up capistrano

* In your Gemfile add:
    > gem 'capistrano'
    > gem 'capistrano-rails'
    > gem 'capistrano-passenger'
    > gem 'capistrano-rbenv' (if you used rvm use: gem 'capistrano-rvm')

* Run `$ bundle install` and then `$ cap install`

If you want to customize this install consult the capistrano docs.

* In `config/deploy.rb` change the following lines, updating them to the name and repo of the current application:
  <pre>
    set :application, "YOUR_APPS_NAME"
    set :repo_url, "YOUR_APPS_REPO"

    set :deploy_to, "/home/deploy/YOUR_APPS_NAME"

    append :linked_files, "config/database.yml", "config/secrets.yml"

    append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system"
  </pre>

* In `config/deploy/production.rb` change the IP address of the server to the IP of the new droplet
  <pre>
    server '104.131.189.221', user: 'deploy', roles: %w{app db web}
  </pre>

* You can then use the command `cap production deploy` to deploy new versions of the app

  * The first time you try this it will fail and throw an error, but we're almost there.

### Home Stretch

SSH back into the server as deploy and create 2 files
* `/home/deploy/APP_NAME/shared/config/database.yml`
  * In this file add the following:
  <pre>
    production:
      adapter: postgresql
      host: 127.0.0.1
      database: APP_NAME_production
      username: deploy
      password: YOUR_POSTGRES_PASSWORD (NOT YOUR LINUX PASSWORD)
      encoding: unicode
      pool: 5
  </pre>

* `/home/deploy/APP_NAME/shared/config/secrets.yml`
  * In this file add the following:
  <pre>
  production:
    secret_key_base: YOUR_SECRET_KEY
  </pre>
(Note: I'm fairly certain there is a better way to do this in Rails 5.2 but I haven't personally used that in production yet)

Run `$ sudo visudo -f /etc/sudoers.d/nginx_overrides` and add the following to the file:
<pre>
  # Nginx Commands
  Cmnd_Alias NGINX_RESTART = /usr/sbin/service nginx restart
  Cmnd_Alias NGINX_RELOAD  = /usr/sbin/service nginx reload

  # No-Password Commands
  deploy ALL=NOPASSWD: NGINX_RESTART, NGINX_RELOAD
</pre>

### Updating the URL

Last this to do on the server is to tell Nginx to respond with the app
* Run `sudo ln -s /etc/nginx/sites-available/THE_URL_YOU_WANT_HERE.com /etc/nginx/sites-enabled/`
* Then `sudo vim /etc/nginx/sites-enabled/THE_URL_YOU_WANT_HERE.com` and replace the files contents with:
<pre>
server {
      listen 80;
      listen [::]:80 ipv6only=on;

      server_name THE_URL_YOU_WANT_HERE.com;
      passenger_enabled on;
      rails_env    production;
      root         /home/deploy/APP_NAME/current/public;

      # redirect server error pages to the static page /50x.html
      error_page   500 502 503 504  /50x.html;
      location = /50x.html {
          root   html;
      }
}
</pre>
* Back on your local machine run `$ cap production deploy` once more and then you should be in business

* If you are still seeing the default Nginx page, run `$ cap production server:restart`

If I missed anything or there's something I did wrong (or could do better) please let me know.  Obviously there's a lot more to running a 'real' production server, logs, security, load balancing, etc, but this will get your app out into the world.  Plus it feels pretty cool to configure your own server.