2016-02-03

Introduction: Deploying Rails as a n00b

As a relative n00b to the world of web development, I don't have much experience deploying production-level apps. As a student at the Flatiron School, I became familiar with using Heroku to deploy and host my Rails applications. After graduating Flatiron, I continued to use Heroku for both Rails and Ember deployments. It's an amazing platform that makes deployment easy for beginners like myself. I use it for my own deployments and I always recommend that my students at Flatiron use it.

Heroku does have some limitations, however. It provides limited database storage and limited support for file uploads and temporary file storage. Hosting our apps on our own servers, on the other hand, gives us total control over this and much more. So, I began exploring DigitalOcean.

I first tried to deploy a Rails application using the 1-click Ruby on Rails installation that DO offers. I failed. Hard. A friend then turned me on to using Dokku to deploy to DigitalOcean. Dokku is "Docker powered mini-Heroku". It allows you to deploy your app to your very own DigitalOcean server using git. It abstracts away most of the nitty-gritty work of configuring your server and your app for production. For someone who knows very little about that nitty-gritty work (me), or anyone who doesn't wan't to engage in unnecessary nitty-gritty technical work (everyone ever), it's the perfect tool. If you're interested in using Dokku to deploy to your DO server, I highly recommend this blog post by my friend and former classmate, Dan Visintainer.

After successfully using Dokku a handful of times for my DigitalOcean deployments, I felt the time had come to stop avoiding the technical nitty-gritty. I often caution my students to avoid using certain gems, like Devise, until they understand the functionality that the gem is offering and can build it out for themselves. Over this past weekend, I decided it was time to practice what I preach, and deploy to DigitalOcean, the hard way. No Dokku, no 1-click installation, just me, my droplet, and a million different tutorials.

After several hours of piecing together 6+ blog posts and articles, I had finally deployed a super-important cat cataloguing app:



As someone that's never deployed to DigitalOcean without the help of Dokku, I found myself really feeling the lack of a guide that would take me through the process from square zero to finish. So, I decided to make one myself!

Part I: Configuring The Server

Create Your Droplet

Once you sign up for your DO account, click the "create droplet" button. Create a new droplet with an Ubuntu installation:



Then, choose a size and a data center region:

.

Lastly, add your SSH key to your droplet so that you can access your server using SSH, and NOT by typing in your password every time.

Click the "add new" tab under the Add SSH Key heading. Grab your public key by running cat ~/.ssh/id_rsa.pub
in your terminal and pasting it into the form field.

Click "Create" and you'll be taken to the dashboard to view your new droplet!

Now we're ready to set up our server.

Setting Up Your Server

Before we do anything else, we need to configure our server. We'll be setting up an additional user through which we'll be interacting with our server. This is a safer approach because we'll be effectively sandboxing our user and allowing them to interact with (i.e. install things into, set-up and change the environment of) a smaller portion of our server.

For this portion of my journey to deployment, I found this article to be particularly helpful and the steps outlined below are largely cribbed from it.

Step 1: Login as Root with SSH

In your terminal, ssh into your server as the root user:

You can find your server IP address by visiting https://cloud.digitalocean.com/droplets:

If this is the first time you're connecting to your server, you'll see the following:

Your computer is telling you that the server to which you are connecting is not recognized. Type yes and hit enter!

And we're in! If you need help troubleshooting any of these steps, check out this article

What is the Root User?

The root user is the administrative user in a Linux environment that has very broad privileges. Because of the heightened privileges of the root account, you are actually discouraged from using it on a regular basis. This is because part of the power inherent with the root account is the ability to make very destructive changes, even by accident.*

For this reason, we'll be setting up another user with a reduced scope of influence. This is the user we'll be logging in as when we move forward with deploying our app.

Step 2: Create The Alternative User

Okay, let's create that user. Make sure you are logged in as the root user and run the following:

We'll call our user "rails" because we'll be deploying a Rails app. You can call your user whatever you want, btw.

You'll be prompted to add a password. Pick a strong password, enter it and hit "enter". You can skip filling in the additional info you're prompted to enter.

We've just created a new account with normal, i.e. not admin, or root, privileges. However, we will want our user to be able to run certain administrative commands. So, we'll give our normal user "super user" privileges. This will allow us to run administrative commands, preceded by sudo.

On Ubuntu, users in the "sudo" group are allowed to use the sudo command. Let's add our "rails" user to that group. Still logged in as the root user, run:

Now we're ready for the next step.

Step 3: Add SSH to the new user

Let's set up SSH authentication for our new user. This way, can log in to this user securely using our SSH public key, rather than entering a password every time.

Open another window in your terminal so that you are interacting with your local machine, not the DigitalOcean server to which you are connected as the root user. Then run:

Copy the public key that is outputted to the terminal. Now, switch back to the terminal window in which you are connected to your DO droplet and execute the following command:

Then, create a new directory and set its permissions:

The 700 permission code means the owner of the file can read, write and execute.

Then, create a file in .ssh and open it in the text editor:

Paste your public key here and then save and exit the file with CTRL-X, then Y, the ENTER.

Lastly, set the permissions of the authorized_keys file to read and write with:

Now, run the following command, just once, to return to the root user:

Now that we've returned to operating at the root user, let's configure out server to disallow future SSH log-ins as root. This is more secure––we'll only allow ourselves to interact with our server as the sudo user, which we've called "rails". This user is somewhat cordoned off, or sandboxed, within our server. We're therefore interacting with our server more securely, because we're interacting with it via a user that does not have root privileges and therefore cannot execute certain destructive commands. In the future, should we need to exercise some of those privileges, we can scale up the permissions of our "rails" user.

Step 4: Configure SSH For the Root User

As the root user, execute the following to open your ssh configuration file in your text editor:

In this file, find the following line:

And change the yes, to no. Save and exit with CTRL + X, then Y, then ENTER.

Now, restart your SSH daemon with:

And that's it!

But, before you log out of the root user, let's confirm that we've configured everything correctly.

Open up another terminal window and try SSH-ing into your server as the rails user:

You'll be prompted to enter the password you set for this user, but when we log in in the future, we'll be able to skip this step. Now that we're successfully logged in as the rails user, go back to the window in which you are logged in as root and type exit.

Now that our rails user is all set up, we're ready to get our environment ready to run a Rails app. Note that for the remainder of this tutorial, you should be logged in to your server as the "rails" user.

Setting Up Our Environment For Rails

We'll be using rbenv to install and manage our Ruby versions. This section of the tutorial is mainly drawn from this article.

rbenv is lighter weight and more flexible for RVM, and thus a better fit for our remote server in which we won't be doing a lot of development, just deploying applications we've developed on our local machine.

Why is rbenv lighter than RVM? Well, it doesn't install Ruby (we get to do that manually, and to our own specifications) and it doesn't manage gemsets (we'll use Bundler for that). It also doesn't have a configuration file.

rbenv has one job: manage Ruby installations and switch between them. It does so by creating shim files in ~./rbenv/shims which retrieve the correct version of Ruby, as specified by your application. ~./rbenv/shims get added to your PATH, meaning there is no need for rbenv to be loaded into your shell (unlike RVM). To learn more about rbenv and how it compares to RVM, check out the rbenv documentation and this excellent article.

Now that we're all convinced that rbenv is the right tool for the job, let's use it!

Step 1: Installing rbenv

First, we need update apt-get. For those of you who are used to working wit OS X, apt-get is the package management command line program that works with Ubuntu's APT (Advanced Packaging Tool) library.

Make sure you are logged in to your DigitalOcean droplet as the rails user and run:

Now, install the rbenv and Ruby dependencies, including openssl and sqlite3.

Now, install rbenv with the following commands:

This will do two things for us: install rbenv into the home directory and set the environment variables that will allow rbenv to deliver the right version of Ruby.

Step 2: Installing Ruby

Install Ruby with the following:

This first installs Ruby and then sets the global version of Ruby, or the version that will be used in all of our shells.

Make sure that everything was installed properly with:

Let's configure it so that Rubygems will not install documentation locally for every gem that we install:

Lastly, we'll install bundler:

Now we're ready to install Rails!

Step 3: Installing Rails

This part is easy:

Then, run

Any time we install a gem that includes binaries, or executable scripts, you need to run rbenv rehash so that rbenv can create the necessary shim files.

When we install a gem, provided that gem has executables, binstub files will get generated. Binstubs are wrappers around executables that prepare the environment before calling on the original executable. The shims directory that rbenv adds to $PATH contains binstubs for every executable related to Ruby.

To learn more, check out this section of the rbenv documentation.

We're almost done setting up Ruby and Rails!

Step 6: Installing Javascript Runtime

Recall that the asset pipeline depends on Javascript runtime. So, we'll need to install Node.js.

First, add the Node PPA (Personal Package Archives) to apt-get

Then, update apt-get and install Node

Last but not least, we need to configure Git.

Step 7: Configure Git

We need to do two things before we are ready to clone down our app and get it up and running on our server in production mode.

Git embeds our username and password in our commits, so let's set up our username and password for GitHub, here on our server.

Now, let's set up SSH with GitHub. First, we'll need to generate a public key for our "rails" user account on our server.

Logged in as "rails" user, run

This will output:

Hit ENTER to confirm the name of the file to which the SSH key will be written. You'll then be prompted for a passphrase. You can leave this blank and hit ENTER, twice.

Copy your public key via cat /home/rails/.ssh.id_rsa.pub. Then, go to GitHub, open your settings and add this new SSH key.

Okay, now we're ready to install PostgreSQL onto our server.

Installing PostgreSQL

For this portion, I worked primarily with this article.

First, update apt-get

Then, run the following:

Later, after we clone down our app and bundle install, we'll log into our Postgres server to create a new user and database and matches up to the user and database specified in the config/database.yml file in our Rails app. For now, we're ready to move on to installing Passenger and Nginx.

Part II: Install and Configure Phusion Passenger and Ngnix

This section of the tutorial owes thanks to this excellent article.

For this deployment, we'll be using Phusion Passenger as our web server. Passenger is better at handling a higher volume of traffic than other popular web servers, like Unicorn or Puma. And, as you may have realized, our cat cataloguing website is extremely important and likely to receive lots of traffic.

Installing Passenger and Nginx

First, we need to install a PGP key

Create an APT source file:

Add this line to the above file:

Save and exit with CTRL + X, then Y, then ENTER.

Update the owner and permissions of the file;

Update apt-get:

Lastly, install Passenger with Ngnix

This last bit may overwrite the Ruby version to an older one. If so, you need to remove the incorrect Ruby location and create a new symlink to the correct Ruby binary file:

Configuring the Web Server

Open the Nginx configuration file in your text editor:

Find and uncomment the following lines:

Then, update the path in the passenger_ruby line. When you're done, it should look like this:

Save and exit with CTRL + X, then Y, then ENTER.

Okay, now we're ready to upload our existing Rails app and configure it for production with Nginx.

Part III: Configuring Rails for Production

We're assuming we have a Rails app that is ready for deployment, just waiting for us and hanging out on GitHub.

Step 1: Clone Your App

Since we configured git correctly earlier on in this tutorial (you may not remember, because this process is taking forever), we should be able to clone down our app int our DigitalOcean server:

Once the app is cloned in, run bundle install.

Debugging Bundle Install If you get errors regarding missing gems when you run this command, execute the following:

Step 2: Set Your Production Secrets

cd into your app:

Then run rake secret. You should see a generated secret key base and token generated for you:

Then, open up the config/secrets.yml file with your text editor:

and set your secret key base and token like this:

Step 3: Configure the Database for Production

Add the following section to your config/database.yml file (via nano config/database.yml):

Save and exit.

Now, we need to create the database with the user and password specified above. Connect to Postgres by typing

If you get an error similar to the following:

Create the user:

Then, you should be connected to Postgres. Your bash prompt should now look like this:

Create the database, user and password to match what you specified in the database.yml file:

Then, exit Postgres with

Step 4: All the Rakes

Run:

Step 5: Configure Ngnix for your App

First, disable the default Nginx configuration.

Comment out the following two lines:

Save and exit.

Now we need to create a custom Nginx configuration file for our app:

To this file, we need to add a server block that:

enables listening on port 80

sets the domain name (in this case the IP address of our droplet)

enables Passenger

sets the root to the directory of our app.

Save and exit with CTRL + X, Y, ENTER.

Create a symlink for it:

Part IV: Deploy!

We're almost done for real!

Lastly, restart Nginx:

Guess what? We're done!!

Visit: http://your-ip-address

and see your glorious app.

Sources

Initial Server Setup with Ubuntu

How to Connect To Your Droplet with SSH

rbenv on GitHub

How to Install Ruby on Rails with rbenv on Ubuntu

How to Install and Use Postgresql on Ubuntu

How to Deploy a Rails App with Passenger and Nginx on Ubuntu

Show more