Ruby on Rails 4.1 Ubuntu 14.04 Server Configuration

Learn how to properly configure a Ubuntu 14.04 server to deploy a Ruby on Rails 4 application. I show you how to install and configure ruby using rbenv, a postgresql database, a nginx web server and a unicorn ruby application server.

Part 2 of 3

This tutorial is the second in a three part series on how to deploy a Ruby on Rails application to your own server.

This tutorial assumes you already have already secured your server. If you have not done that yet, you can learn how in our Securely Setup Ubuntu 14.04 Server video.

On your server, install packages and setup application user and directories.

# install required packages
sudo aptitude -y install git-core curl build-essential zlib1g-dev libssl-dev libreadline-dev libxml2-dev libxslt1-dev libcurl4-openssl-dev nodejs postgresql postgresql-server-dev-9.3 python-software-properties

# install nginx ppa, update and install nginx
sudo add-apt-repository ppa:nginx/stable
sudo aptitude update
sudo aptitude -y install nginx

# create a user for your application to run under
sudo adduser deploy --disabled-password

# create an .ssh directory to setup public key authentication
sudo mkdir /home/deploy/.ssh
sudo chown -R deploy /home/deploy/.ssh
sudo chgrp -R deploy /home/deploy/.ssh
sudo chmod -R 700 /home/deploy/.ssh

# Copy your public key to the deploy user's authorized keys
# You may want to append your key instead
sudo cp ~/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
sudo chown deploy /home/deploy/.ssh/authorized_keys
sudo chgrp deploy /home/deploy/.ssh/authorized_keys

# Optional: add the deploy group to your user
sudo usermod -a -G deploy creston

# create the directory to deploy your application
sudo mkdir -p /opt/www/testapp 
sudo chown -R deploy /opt/www/testapp
sudo chgrp -R deploy /opt/www/testapp
sudo chmod -R 775 /opt/www/testapp

On your server, install ruby.

# Assume the deploy user
sudo su - deploy

# install rbenv
git clone https://github.com/sstephenson/rbenv.git ~/.rbenv

# Configure .bashrc file
nano ~/.bashrc
# Add to the top of .bashrc
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"
# re-execute your shell 
exec $SHELL 

# install ruby-build 
git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build 

# choose ruby version, install and configure 
rbenv install -l 
rbenv install 2.1.4 
rbenv global 2.1.4 
rbenv rehash 
ruby -v 

# install bundler gem 
gem install bundler --no-ri --no-rdoc 
rbenv rehash 

# exit deploy user
exit

On your server, configure postgres.

# assume the postgres user
sudo su - postgres

# connect to postgres
psql

# create a role/user that matches the user you created earlier
create user deploy;

# create a database with your new role/user as the owner
create database testapp_production owner deploy;

# exit psql and the postgres user
\q
exit

# test the db connection by assuming the deploy user and then connecting
sudo su - deploy
psql -d testapp_production

# after connecting, exit
\q
exit

On your server, configure nginx.

# remove the default nginx site and create a new one
sudo rm /etc/nginx/sites-enabled/default
sudo nano /etc/nginx/sites-available/testapp

testapp nginx site configuration

upstream unicorn_testapp {
  server unix:/tmp/unicorn.testapp.sock fail_timeout=0;
}

server {
  listen 80 default deferred;
  server_name testapp.rubytreesoftware.com;
  root /opt/www/testapp/current/public;

  location ~ ^/assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

  try_files $uri/index.html $uri @unicorn;
  location @unicorn {
    proxy_set_header Host $http_host;  
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_redirect off;
    proxy_pass http://unicorn_testapp;
  }

  error_page 500 502 503 504 /500.html;
  client_max_body_size 4G;
  keepalive_timeout 10;
}
# enable your new site
sudo ln -s /etc/nginx/sites-available/testapp /etc/nginx/sites-enabled/testapp

# Start nginx
sudo service nginx start

On your server, configure unicorn.

# setup init script for unicorn
sudo nano /etc/init.d/unicorn_testapp

unicorn init.d script

#!/bin/sh
### BEGIN INIT INFO
# Provides:          unicorn
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Manage unicorn server
# Description:       Start, stop, restart unicorn server for a specific application.
### END INIT INFO
set -e

# Feel free to change any of the following variables for your app:
TIMEOUT=${TIMEOUT-60}
APP_ROOT=/opt/www/testapp/current
PID=$APP_ROOT/tmp/pids/unicorn.pid
CMD="cd $APP_ROOT; bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E production"
AS_USER=deploy
set -u

OLD_PIN="$PID.oldbin"

sig () {
  test -s "$PID" && kill -$1 `cat $PID`
}

oldsig () {
  test -s $OLD_PIN && kill -$1 `cat $OLD_PIN`
}

run () {
  if [ "$(id -un)" = "$AS_USER" ]; then
    eval $1
  else
    su -c "$1" - $AS_USER
  fi
}

case "$1" in
start)
  sig 0 && echo >&2 "Already running" && exit 0
  run "$CMD"
  ;;
stop)
  sig QUIT && exit 0
  echo >&2 "Not running"
  ;;
force-stop)
  sig TERM && exit 0
  echo >&2 "Not running"
  ;;
restart|reload)
  sig HUP && echo reloaded OK && exit 0
  echo >&2 "Couldn't reload, starting '$CMD' instead"
  run "$CMD"
  ;;
upgrade)
  if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
  then
    n=$TIMEOUT
    while test -s $OLD_PIN && test $n -ge 0
    do
      printf '.' && sleep 1 && n=$(( $n - 1 ))
    done
    echo

    if test $n -lt 0 && test -s $OLD_PIN
    then
      echo >&2 "$OLD_PIN still exists after $TIMEOUT seconds"
      exit 1
    fi
    exit 0
  fi
  echo >&2 "Couldn't upgrade, starting '$CMD' instead"
  run "$CMD"
  ;;
reopen-logs)
  sig USR1
  ;;
*)
  echo >&2 "Usage: $0 "
  exit 1
  ;;
esac

On server, finalize unicorn configuration.

# Set permissions for init.d script
sudo chmod 755 /etc/init.d/unicorn_testapp

# Set the init.d script to start on server start
sudo update-rc.d unicorn_testapp defaults

Please go ahead and leave a comment below if you have any questions about this tutorial.