Deploy Ruby On Rails on
Ubuntu 16.04 Xenial Xerus

A guide to setting up a Ruby on Rails production environment


Want an automated way to configure a server and deploy Rails apps to it? Check out Hatch!

This will take about 45 minutes.

We will be setting up a Ruby on Rails production environment on Ubuntu 16.04 LTS Xenial Xerus.

Since we setup Ubuntu for our development environment, we also want to use it in production. This keeps your application running consistently between development and production. We're using an LTS version of Ubuntu in production because it is supported for several years where a normal version of Ubuntu isn't.

Using Ubuntu LTS in production allows you to continue receiving security updates which is important for your production server(s).

We're going to be setting up a Droplet on Digital Ocean for our server. It costs $5/mo and is a great place to host your applications.

If you sign up with my Digital Ocean referral link, you'll get 2 months ($10) free credit to try it out.

You can use any cloud server hosting company you choose for your Rails application. I've had excellent experience with Digital Ocean and Linode with the servers I have used. If you're looking for alternatives outside the US or otherwise, just google "VPS hosting". A VPS is a virtual private server. It's just like a server you setup at home, only virtualize and running with a suite of other servers in a datacenter.

First, we need to choose which OS to use. We're going to be using Ubuntu 16.04 LTS x64. Your application may require a different OS or version, but if you're not sure this is generally what you should use.

Droplet Image

Since we're using Digital Ocean for our cloud server, the first thing we're going to do is configure a new one. I'm going with the Droplet with 1GB of RAM. You can setup whichever size server you prefer, keep in mind that if you choose a 512MB server you may run into some slowness with a low amount of RAM.

Droplet Size

The next step is to choose your location. Choose one close to you so that you can have better connection speeds.

Droplet Region

Optionally you can add your SSH key into the Droplet so you can SSH in and skip the ssh-copy-id step.

Once Digital Ocean has configured your server, check your email to get your password for the new cloud server.

You should follow the instructions in the email to login via SSH for the very first time and verify it is working.

The first thing we will do on our new server is create the user account we'll be using to run our applications and work from there.

sudo adduser deploy
sudo adduser deploy sudo
su deploy

Before we move forward is that we're going to setup SSH to authenticate via keys instead of having to use a password to login. It's more secure and will save you time in the long run.

We're going to use ssh-copy-id to do this. If you're on OSX you may need to run brew install ssh-copy-id but if you're following this tutorial on Linux desktop, you should already have it.

Once you've got ssh-copy-id installed, run the following and replace IPADDRESS with the one for your server:

Make sure you run ssh-copy-id on your computer, and NOT the server.

ssh-copy-id [email protected]

Now when you run ssh [email protected] you will be logged in automatically. Go ahead and SSH again and verify that it doesn't ask for your password before moving onto the next step.

For the next steps, make sure you are logged in as the deploy user on the server!

Choose the version of Ruby you want to install:

The first step is to install some dependencies for Ruby.

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 python-software-properties libffi-dev nodejs

Next we're going to be installing Ruby using one of three methods. Each have their own benefits, most people prefer using rbenv these days, but if you're familiar with rvm you can follow those steps as well. I've included instructions for installing from source as well, but in general, you'll want to choose either rbenv or rvm.

Choose one method. Some of these conflict with each other, so choose the one that sounds the most interesting to you, or go with my suggestion, rbenv.

Installing with rbenv is a simple two step process. First you install rbenv, and then ruby-build:

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

rbenv install 2.4.0
rbenv global 2.4.0
ruby -v

The installation for rvm is pretty simple:

sudo apt-get install libgdbm-dev libncurses5-dev automake libtool bison libffi-dev
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
curl -sSL https://get.rvm.io | bash -s stable
source ~/.rvm/scripts/rvm
rvm install 2.4.0
rvm use 2.4.0 --default
ruby -v

Arguably the least useful Ruby setup for development is installing from source, but I thought I'd give you the steps anyways:

cd
wget http://ftp.ruby-lang.org/pub/ruby/2.4/ruby-2.4.0.tar.gz
tar -xzvf ruby-2.4.0.tar.gz
cd ruby-2.4.0/
./configure
make
sudo make install
ruby -v

The last step is to install Bundler

gem install bundler

rbenv users need to run rbenv rehash after installing bundler.

Phusion is the company that develops Passenger and they recently put out an official Ubuntu package that ships with Nginx and Passenger pre-installed.

We'll be using that to setup our production server because it's very easy to setup.

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

# Add Passenger APT repository
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

# Install Passenger & Nginx
sudo apt-get install -y nginx-extras passenger

So now we have Nginx and passenger installed. We can manage the Nginx webserver by using the service command:

sudo service nginx start

Open up the server's IP address in your browser to make sure that nginx is up and running.

The service command also provides some other methods such as restart and stop that allow you to easily restart and stop your webserver.

Next, we need to update the Nginx configuration to point Passenger to the version of Ruby that we're using. You'll want to open up /etc/nginx/nginx.conf in your favorite editor. I like to use vim, so I'd run this command:

sudo vim /etc/nginx/nginx.conf

# You could also use nano if you don't like vim
# sudo nano /etc/nginx/nginx.conf

Find the following lines, and uncomment them:

##
# Phusion Passenger
##
# Uncomment it if you installed ruby-passenger or ruby-passenger-enterprise
##

include /etc/nginx/passenger.conf;

Save and close nginx.conf. Then open /etc/nginx/passenger.conf in your editor to modify the ruby line:

sudo vim /etc/nginx/passenger.conf

# You could also use nano if you don't like vim
# sudo nano /etc/nginx/passenger.conf

And change the passenger_ruby line to point to your ruby executable:

passenger_ruby /home/deploy/.rbenv/shims/ruby; # If you use rbenv
# passenger_ruby /home/deploy/.rvm/wrappers/ruby-2.1.2/ruby; # If use use rvm, be sure to change the version number
# passenger_ruby /usr/bin/ruby; # If you use ruby from source

The passenger_ruby is the important line here. Make sure you only set this once and use the line from the example that pertains to the version of Ruby you installed.

Once you've changed passenger_ruby to use the right version Ruby, you can run the following command to restart Nginx with the new Passenger configuration.

sudo service nginx restart

Now that we've restarted Nginx, the Rails application will be served up using the deploy user just how we want. In the Capistrano section we will talk about configuring Nginx to serve up your Rails application.

Setting up your production database is pretty easy. Make sure to keep in mind that you should use a different password for your production databases.

Depending on what database you want to use, follow the steps related to the database:

Installing MySQL

All you need to do in order to install MySQL is to run the following command:

sudo apt-get install mysql-server mysql-client libmysqlclient-dev

You can use the root user and password set during installation for your database or you can add a new user to MySQL.

Make sure you also create your app's database now. If you're not sure how to do this, follow this guide to create your mysql database. Take note of your database name and password so you can use this when we setup the database.yml file later on.

Installing PostgreSQL

Postgres 9.3 is available in the Ubuntu repositories and we can install it like so:

sudo apt-get install postgresql postgresql-contrib libpq-dev

Next we need to setup our postgres user (also named "deploy" but different from our linux user named "deploy") and database:

sudo su - postgres
createuser --pwprompt deploy
createdb -O deploy my_app_name_production # change "my_app_name" to your app's name which we'll also use later on
exit

The password you type in here will be the one to put in your my_app/current/config/database.yml later when you deploy your app for the first time.

For Capistrano, make sure you do these steps on your development machine inside your Rails app.

The first step is to add Capistrano to your Gemfile:

gem 'capistrano', '~> 3.7', '>= 3.7.1'
gem 'capistrano-rails', '~> 1.2'
gem 'capistrano-passenger', '~> 0.2.0'

# Add this if you're using rbenv
# gem 'capistrano-rbenv', '~> 2.1'

# Add this if you're using rvm
# gem 'capistrano-rvm'

Once these are added, run the following command to generate your capistrano configuration.

cap install STAGES=production

Next we need to make some additions to our Capfile to include bundler, rails, and rbenv/rvm (if you're using them). Edit your Capfile and add these lines:

require 'capistrano/rails'
require 'capistrano/passenger'

# If you are using rbenv add these lines:
# require 'capistrano/rbenv'
# set :rbenv_type, :user
# set :rbenv_ruby, '2.4.0'

# If you are using rvm add these lines:
# require 'capistrano/rvm'
# set :rvm_type, :user
# set :rvm_ruby_version, '2.4.0'

After we've got Capistrano installed, we can configure the config/deploy.rb to setup our general configuration for our app. Edit that file and make it like the following replacing "myapp" with the name of your application and git repository:

set :application, "my_app_name"
set :repo_url, "[email protected]:me/my_repo.git"

set :deploy_to, '/home/deploy/my_app_name'

append :linked_files, "config/database.yml", "config/secrets.yml"
append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "vendor/bundle", "public/system", "public/uploads"

Now we need to open up our config/deploy/production.rb file to set the server IP address that we want to deploy to:

# Replace 127.0.0.1 with your server's IP address!
server '127.0.0.1', user: 'deploy', roles: %w{app db web}

If you have any trouble with Capistrano or the extensions for it, check out Capistrano's Github page.

Thankfully there aren't a whole lot of things to do left!

Adding The Nginx Host

In order to get Nginx to respond with the Rails app, we need to modify it's sites-enabled.

Open up /etc/nginx/sites-enabled/default in your text editor and we will replace the file's contents with the following:

server {
        listen 80;
        listen [::]:80 ipv6only=on;

        server_name mydomain.com;
        passenger_enabled on;
        rails_env    production;
        root         /home/deploy/my_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;
        }
}

This is our Nginx configuration for a server listening on port 80. You need to change the server_name values to match the domain you want to use and in root replace "myapp" with the name of your application.

Connecting The Database

One optional thing I would recommend is to remove your config/database.yml and config/secrets.yml git and only store example copies in your git repo. This way we can easily copy the files for setting up development, but our production environment can symlink files on the server so that our production secrets and passwords are only stored on the production server.

First we'll move these files to their example names in the git repo.

echo "config/database.yml\nconfig/secrets.yml" >> .gitignore
git add .gitignore
git mv config/secrets.yml config/secrets.yml.example
git mv config/database.yml config/database.yml.example
git commit -m "Only store example secrets and database configs"
cp config/secrets.yml.example config/secrets.yml
cp config/database.yml.example config/database.yml

You can run cap production deploy to deploy your application, but it's going to fail this first time because we haven't created either of these files on the server which we will do in just a second.

linked file /home/deploy/build.gorails.com/shared/config/database.yml does not exist on IP_ADDRESS

One last time, ssh into your server as the deploy user and this time we need to create two files. First is the database.yml that uses the password for the postgres user you created earlier.

# /home/deploy/my_app_name/shared/config/database.yml
production:
  adapter: postgresql
  host: 127.0.0.1
  database: my_app_name_production
  username: deploy
  password: YOUR_POSTGRES_PASSWORD
  encoding: unicode
  pool: 5

Now we create secrets.yml. We need to run rake secret on your development machine and take the secret key output of that and paste that in as YOUR_SECRET_KEY on the server.

# /home/deploy/my_app_name/shared/config/secrets.yml
production:
  secret_key_base: YOUR_SECRET_KEY

You can run cap production deploy one last time to get your full deployment to run. This should completed successfully and you should see your new site live! You can just run Capistrano again to deploy any new changes you've pushed up to your Git repository.

Restarting The Site

One last thing you should know is that restarting just the Rails application with Passenger is very easy. If you ssh into the server, you can run touch my_app_name/current/tmp/restart.txt and Passenger will restart the application for you. It monitors the file's timestamp to determine if it should restart the app. This is helpful when you want to restart the app manually without deploying it.

Conclusion

And there you have it, a very long-winded explanation of all the different things you need to do while setting up an application to be deployed. There is a lot of system administration pieces that can expand upon this, but that's for another time. Please let me know if you have any questions, comments, or suggestions!