Tutorial

How To Deploy a Rails App with Unicorn and Nginx on Ubuntu 14.04

How To Deploy a Rails App with Unicorn and Nginx on Ubuntu 14.04

Introduction

When you are ready to deploy your Ruby on Rails application, there are many valid setups to consider. This tutorial will help you deploy the production environment of your Ruby on Rails application, with PostgreSQL as the database, using Unicorn and Nginx on Ubuntu 14.04.

Unicorn is an application server, like Passenger or Puma, that enables your Rails application to process requests concurrently. As Unicorn is not designed to be accessed by users directly, we will use Nginx as a reverse proxy that will buffer requests and responses between users and your Rails application.

Prerequisites

This tutorial assumes that you have an Ubuntu 14.04 server with the following software installed, on the user that will deploy the application:

If you do not have that set up already, follow the tutorials that are linked above. We will assume that your user is called deploy.

Also, this tutorial does not cover how to set up your development or test environments. If you need help with that, follow the example in the PostgreSQL with Rails tutorial.

Create Rails Application

Ideally, you already have a Rails application that you want to deploy. If this is the case, you may skip this section, and make the appropriate substitutions while following along. If not, the first step is to create a new Rails application that uses PostgreSQL as its database.

This command will create a new Rails application, named “appname” that will use PostgreSQL as the database. Feel free to substitute the highlighted “appname” with something else:

  1. rails new appname -d postgresql

Then change into the application directory:

  1. cd appname

Let’s take a moment to create the PostgreSQL user that will be used by the production environment of your Rails application.

Create Production Database User

To keep things simple, let’s name the production database user the same as your application name. For example, if your application is called “appname”, you should create a PostgreSQL user like this:

  1. sudo -u postgres createuser -s appname

We want to set the database user’s password, so enter the PostgreSQL console like this:

  1. sudo -u postgres psql

Then set the password for the database user, “appname” in the example, like this:

  1. \password appname

Enter your desired password and confirm it.

Exit the PostgreSQL console with this command:

  1. \q

Now we’re ready to configure the your application with the proper database connection information.

Configure Database Connection

Ensure that you are in your application’s root directory (cd ~/appname).

Open your application’s database configuration file in your favorite text editor. We’ll use vi:

  1. vi config/database.yml

Under the default section, find the line that says “pool: 5” and add the following line under it (if it doesn’t already exist):

  host: localhost

If you scroll to the bottom of the file, you will notice that the production section is set the following:

  username: appname
  password: <%= ENV['APPNAME_DATABASE_PASSWORD'] %>

If your production username doesn’t match the database user that you created earlier, set it now.

Note that the database password is configured to be read by an environment variable, APPNAME_DATABASE_PASSWORD. It is considered best practice to keep production passwords and secrets outside of your application codebase, as they can easily be exposed if you are using a distributed version control system such as Git. We will go over how to set up the database authentication with environment variables next.

Install rbenv-vars Plugin

Before deploying a production Rails application, you should set the production secret key and database password using environment variables. An easy way to manage environment variables, which we can use to load passwords and secrets into our application at runtime, is to use the rbenv-vars plugin.

To install the rbenv-vars plugin, simply change to the .rbenv/plugins directory and clone it from GitHub. For example, if rbenv is installed in your home directory, run these commands:

  1. cd ~/.rbenv/plugins
  2. git clone https://github.com/sstephenson/rbenv-vars.git

Set Environment Variables

Now that the rbenv-vars plugin is installed, let’s set up the required environment variables.

First, generate the secret key, which will be used to verify the integrity of signed cookies:

  1. cd ~/appname
  2. rake secret

Copy the secret key that is generated, then open the .rbenv-vars file with your favorite editor. We will use vi:

  1. vi .rbenv-vars

Any environment variables that you set here can be read by your Rails application.

First, set the SECRET_KEY_BASE variable like this (replace the highlighted text with the secret that you just generated and copied):

SECRET_KEY_BASE=your_generated_secret

Next, set the APPNAME_DATABASE_PASSWORD variable like this (replace the highlighted “APPNAME” with your your application name, and “prod_db_pass” with your production database user password):

APPNAME_DATABASE_PASSWORD=prod_db_pass

Save and exit.

You may view which environment variables are set for your application with the rbenv-vars plugin by running this command:

  1. rbenv vars

If you change your secret or database password, update your .rbenv-vars file. Be careful to keep this file private, and don’t include it any public code repositories.

Create Production Database

Now that your application is configured to talk to your PostgreSQL database, let’s create the production database:

  1. RAILS_ENV=production rake db:create

Generate a Controller

If you are following along with the example, we will generate a scaffold controller so our application will have something to look at:

  1. rails generate scaffold Task title:string note:text

Now run this command to update the production database:

  1. RAILS_ENV=production rake db:migrate

Precompile Assets

At this point, the application should work but you will need to precompile its assets so that any images, CSS, and scripts will load. To do so, run this command:

  1. RAILS_ENV=production rake assets:precompile

Test Application

To test out if your application works, you can run the production environment, and bind it to the public IP address of your server (substitute your server’s public IP address):

  1. RAILS_ENV=production rails server --binding=server_public_IP

Now visit this URL in a web browser:

http://server_public_IP:3000/tasks

If it’s working properly, you should see this page:

Tasks controller

Go back to your Rails server, and press Ctrl-c to stop the application.

Install Unicorn

Now we are ready to install Unicorn.

An easy way to do this is to add it to your application’s Gemfile. Open the Gemfile in your favorite editor (make sure you are in your application’s root directory):

  1. vi Gemfile

At the end of the file, add the Unicorn gem with this line:

gem 'unicorn'

Save and exit.

To install Unicorn, and any outstanding dependencies, run Bundler:

  1. bundle

Unicorn is now installed, but we need to configure it.

Configure Unicorn

Let’s add our Unicorn configuration to config/unicorn.rb. Open the file in a text editor:

  1. vi config/unicorn.rb

Copy and paste this configuration into the file:

# set path to application
app_dir = File.expand_path("../..", __FILE__)
shared_dir = "#{app_dir}/shared"
working_directory app_dir


# Set unicorn options
worker_processes 2
preload_app true
timeout 30

# Set up socket location
listen "#{shared_dir}/sockets/unicorn.sock", :backlog => 64

# Logging
stderr_path "#{shared_dir}/log/unicorn.stderr.log"
stdout_path "#{shared_dir}/log/unicorn.stdout.log"

# Set master PID location
pid "#{shared_dir}/pids/unicorn.pid"

Save and exit. This configures Unicorn with the location of your application, and the location of its socket, logs, and PIDs. Feel free to modify the file, or add any other options that you require.

Now create the directories that were referred to in the configuration file:

  1. mkdir -p shared/pids shared/sockets shared/log

Create Unicorn Init Script

Let’s create an init script so we can easily start and stop Unicorn, and ensure that it will start on boot.

Create a script and open it for editing with this command (replace the highlighted part with your application name, if you wish):

  1. sudo vi /etc/init.d/unicorn_appname

Copy and paste the following code block into it, and be sure to substitute USER and APP_NAME (highlighted) with the appropriate values:

#!/bin/sh

### BEGIN INIT INFO
# Provides:          unicorn
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the unicorn app server
# Description:       starts unicorn using start-stop-daemon
### END INIT INFO

set -e

USAGE="Usage: $0 <start|stop|restart|upgrade|rotate|force-stop>"

# app settings
USER="deploy"
APP_NAME="appname"
APP_ROOT="/home/$USER/$APP_NAME"
ENV="production"

# environment settings
PATH="/home/$USER/.rbenv/shims:/home/$USER/.rbenv/bin:$PATH"
CMD="cd $APP_ROOT && bundle exec unicorn -c config/unicorn.rb -E $ENV -D"
PID="$APP_ROOT/shared/pids/unicorn.pid"
OLD_PID="$PID.oldbin"

# make sure the app exists
cd $APP_ROOT || exit 1

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

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

case $1 in
  start)
    sig 0 && echo >&2 "Already running" && exit 0
    echo "Starting $APP_NAME"
    su - $USER -c "$CMD"
    ;;
  stop)
    echo "Stopping $APP_NAME"
    sig QUIT && exit 0
    echo >&2 "Not running"
    ;;
  force-stop)
    echo "Force stopping $APP_NAME"
    sig TERM && exit 0
    echo >&2 "Not running"
    ;;
  restart|reload|upgrade)
    sig USR2 && echo "reloaded $APP_NAME" && exit 0
    echo >&2 "Couldn't reload, starting '$CMD' instead"
    $CMD
    ;;
  rotate)
    sig USR1 && echo rotated logs OK && exit 0
    echo >&2 "Couldn't rotate logs" && exit 1
    ;;
  *)
    echo >&2 $USAGE
    exit 1
    ;;
esac

Save and exit. This will allow you to use service unicorn_appname to start and stop your Unicorn and your Rails application.

Update the script’s permissions and enable Unicorn to start on boot:

  1. sudo chmod 755 /etc/init.d/unicorn_appname
  2. sudo update-rc.d unicorn_appname defaults

Let’s start it now:

  1. sudo service unicorn_appname start

Now your Rails application’s production environment is running under Unicorn, and it’s listening on the shared/sockets/unicorn.sock socket. Before your application will be accessible to an outside user, you must set up the Nginx reverse proxy.

Install and Configure Nginx

Install Nginx using apt-get:

  1. sudo apt-get install nginx

Now open the default server block with a text editor:

  1. sudo vi /etc/nginx/sites-available/default

Replace the contents of the file with the following code block. Be sure to replace the the highlighted parts with the appropriate username and application name:

upstream app {
    # Path to Unicorn SOCK file, as defined previously
    server unix:/home/deploy/appname/shared/sockets/unicorn.sock fail_timeout=0;
}

server {
    listen 80;
    server_name localhost;

    root /home/deploy/appname/public;

    try_files $uri/index.html $uri @app;

    location @app {
        proxy_pass http://app;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
    }

    error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10;
}

Save and exit. This configures Nginx as a reverse proxy, so HTTP requests get forwarded to the Unicorn application server via a Unix socket. Feel free to make any changes as you see fit.

Restart Nginx to put the changes into effect:

  1. sudo service nginx restart

Now the production environment of your Rails application is accessible via your server’s public IP address or FQDN. To access the Tasks controller that we created earlier, visit your application server in a web browser:

http://server_public_IP/tasks

You should see the same page that you saw the first time you tested your application, but now it’s being served through Nginx and Unicorn.

Conclusion

Congratulations! You have deployed the production environment of your Ruby on Rails application using Nginx and Unicorn.

If you are looking to improve your production Rails application deployment, you should check out our tutorial series on How To Use Capistrano to Automate Deployments. The series is based on CentOS, but it should still be helpful in automating your deployments.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
10 Comments


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

I’ve set the environment variables following the steps above and when I try to connect to the production database I keep getting this error:

" Missing secret_token and secret_key_base for ‘production’ environment, set these values in config/secrets.yml "

I’ve tried to google an answer but am running around in circles! Here is what I have in my secrets.yml file:

development: secret_key_base: (mysecretkey)

test: secret_key_base: (mysecretkey)

Do not keep production secrets in the repository,

instead read values from the environment.

production: secret_key_base: <%= ENV[“SECRET_KEY_BASE”] %>

And here is the .rbenv-vars file:

SECRET_KEY_BASE=‘mysecretkey’ export YAMAMA_DATABASE_PASSWORD=‘mydatabasepassword’

Any thoughts would be greatly appreciated!

Ok, I’ve made it to the end and want to see if I can get this working before I scrap it and start over - Unicorn and Nginx are both up and running. But when I connect to http://my_public_IP/tasks the browser displays that it can’t establish a connection to the server. I know there are a million things that could’ve gone wrong, but anything off the top of your head I should check to see why it’s not seeing my app?

Thanks for the great tutorial. Took me about a day and a half and rebuilding my server 4 times(plain ubuntu and unicorn+nginx). But finally got it to work. from plain ubuntu. I would suggest adding to your prerequisite postgreSQL tutorial the issues that might arise if locales aren’t set correctly before installing postgresql(fought with that for quite some time until I finally figured what the problem was),

But otherwise great tutorial.

When I get to the part where I install unicorn and try to start it, using:

sudo service unicorn_appname start

I get this error:

Starting appname
-su: bundle: command not found

(I literally named the app “appname”, just for testing purposes, fyi) I’m certain that all users can bundle. I’m lost and this error makes no sense to me. If anybody else has run into this error, I would appreciate your insight. Thanks.

Thanks Mitchell. This worked for me (although I did have to use source ~/.bash_profile instead of exec $SHELL)

One thing that’s not working as expected is re-deploying code. When I issue sudo service unicorn_appname restart, it doesn’t reload the new codebase (models, controllers, config, etc.). The Digital Ocean Nginx/Unicorn/Rails image refreshes the code upon a unicorn restart. Is there something I’m missing?

Also, maybe this is a similar problem, but when I issue sudo service unicorn_appname stop, and then sudo service unicorn_appname start the prompt says “Already running”

Thanks for the great tutorial.

When I try …sudo service unicorn_myapp start. I get this message

/etc/init.d/unicorn_myfriendlyroad: 13: /etc/init.d/unicorn_myfriendlyroad: Syntax error: “(” unexpected

I tried setting up a new droplet and starting over and still get the same message.

Help?!

I managed to fix the bash issue. However, I can’t get my site to run on port 80. I can see it on http:myapp.com:8080 but not on port 80. The connection is refused. I have a UFW and port 80 is added. I will post a question on the community board, seperately.

My nginx configuration followed the tutorial exactly, when I enter example.com in my browser, I get ERR_CONNECTION_REFUSED. If I add port 8080 example.com:8080, I can see my site. In dev, I was able to get it working on port 3000 using

My nginx configuration follows the configuration in the tutorial. I think it has something to do with the rbenv, or ? When I loaded my production database, I my database.yml would not read my password. By typing rbenv vars in the console, get the exports, but it would not take it when I tried to set up the production database. So, I copied and pasted them in place of the <%=ENV… > in mydatabase.ym. and secrets.yml (not ideal, but wanted to see if it worked). When I run RAILS_ENV=production rails server --binding=my-ip-address I see only HTML When I run sudo service unicorn_myapp start, I get … Starting my app and then bash: cannot set terminal process group (-1): Inappropriate ioctl for device bash: no job control in this shell I logged a service ticket and they say “something isn’t working as expected in the init script unicorn_app”

I stuck with the config in the tutorial for Unicorn.rb and unicorn_myapp and followed the steps in the tutorial. I also made sure I have unicorn in my gems and did a gem install and bundle update to make sure.

I am able to use the command unicorn. (not unicorn_myapp) … and it gets this result I, [2015-06-01T21:22:52.143309 #22595] INFO – : listening on addr=0.0.0.0:8080 fd=9 I, [2015-06-01T21:22:52.144178 #22595] INFO – : worker=0 spawning… I, [2015-06-01T21:22:52.145277 #22595] INFO – : master process ready I, [2015-06-01T21:22:52.146540 #22639] INFO – : worker=0 spawned pid=22639 I, [2015-06-01T21:22:52.147100 #22639] INFO – : Refreshing Gem list I, [2015-06-01T21:22:54.089793 #22639] INFO – : worker=0 ready

Hi there, thanks for the tutorial, although, I can’t actually save my unicorn_appname file because it tells me “Unable to save /etc/init.d/unicorn_klein_and_sons - Error: No such file or directory”. I am using Sublime as an editor is that part of the problem?

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.