Alex Pearwin

An Ubuntu VPS with rbenv, nginx and Passenger

This post has been archived. It's pretty old and likely concerns topics that are avoidable by using more modern tools and techniques. The original text is preserved below for posterity but it may no longer be relevant or correct.

Every once in a while I buy a new VPS, mostly because I’m not entirely satisfied with my current setup. My stack of choice is usually RVM for Ruby management, the Apache web server, Passenger for allowing Apache to run Ruby apps like Rails and Sinatra, and MySQL for the database.

This time around I decided to use some other software that seems to be popular with the community.

  • For the webserver I chose nginx. There are many comparisons of Apache and nginx, but for such a small-scale server as mine it really boiled down to curiosity.
  • RVM has received some flack, principally because of Bundler’s ability to manage “gemsets” with Gemfiles. I went with rbenv to manage my rubies.
  • I stuck with Passenger for allowing the web server to run Ruby apps. Although other solutions exist, like Unicorn and Thin, the configuration required was a little too much for me.
  • PostgreSQL is a popular choice these days. If it’s good enough for Ryan Bates then it’s good enough for me!

Now that the stack’s chosen, it’s just a case of setting it all up.

Setting Up Ubuntu

In this section all commands are executed as the root user, as when you log in to a fresh install. If you’ve already got a user set up, prefix all the commands in this section with sudo.

I chose Ubuntu as my Linux distribution of choice. The lastest Ubuntu image provided by my VPS host was 11.04, so first I had to update Ubuntu to the latest LTS, 12.04. First, make sure we’re up-to-date.

$ apt-get -y update
$ apt-get -y upgrade

The -y flag assumes a ‘yes’ response for all prompts. Next, as there’s no direct upgrade route from 11.04 to 12.04, we need to go via 11.10.

$ apt-get install update-manager-core
$ do-release-upgrade

Confirm that you want to perform the upgrade, and once that’s finished and the VPS restarted (reboot) Ubuntu will be at 11.10. You can check this:

$ lsb_release -a
...
Release:        11.10

Perform do-release-upgrade again to arrive at 12.04 LTS.

For a basic setting up of our VPS, the Slicehost article on setting up Ubuntu is great. In particular, make sure to set up the wheel admin group and get iptables set up. We’ll add a user to the wheel group later.

Once that’s done, we have a non-root user with which to log in to the VPS. Log in as that user from your local machine.

$ ssh user@12.345.23.91

Welcome to Ubuntu 12.04 LTS (GNU/Linux 2.6.38-8-generic x86_64)
$ ...

Create a user to deploy with and add her to the wheel group.

$ sudo useradd -G wheel deploy

Where deploy is the username of our new user. Finally, we’ll install a few dependencies for later.

$ sudo apt-get -y install curl git-core python-software-properties build-essential zlib1g-dev libssl-dev libreadline-gplv2-dev libcurl4-openssl-dev 

Installing the Components

We have four main components to install: Ruby, PostgreSQL, Passenger, and nginx. We’ll install them in that order, as Passenger comes with a handy command that installs nginx from source for us.

To install Ruby we’ll install rbenv, which allows us to manage multiple rubies on the same machine. As deploy will be deploying the apps, we need her to have access to the rubies. Assume access as deploy:

$ su - deploy
Password:

And install rbenv using the handy rbenv installer:

$ curl https://raw.github.com/fesplugas/rbenv-installer/master/bin/rbenv-installer | bash

Add the snippet the installer gives you to your .bashrc and source the file in the shell.

$ nano .bashrc # or any other editor
...
$ . .bashrc

To make sure we’ve got all the dependencies required for installing Ruby from source, run rbenv bootstrap-ubuntu-12-04. We’ll install the latest version which is, at the time of writing, 1.9.3 patch 194.

$ rbenv install 1.9.3-p194
...
$ rbenv global 1.9.3-p194

The last command makes 1.9.3-p194 the global, default version of Ruby. You can check the Ruby version with ruby -v.

Now we have Ruby, we can install Passenger as a gem and then refresh rbenv.

$ gem install passenger
$ rbenv rehash

Next, we install nginx.

$ sudo passenger-install-nginx-module

The installer will figure out if all the dependencies are preset. If they’re not, it’ll give you a snippet with which to install them. Choose option 1 to let Passenger download, configure, and compile nginx. The default installation directory /opt/nginx is fine, but some people prefer the more familiar Apache-like directory /etc/nginx.

Once that’s installed, we’ll get a script for nginx that allows us to use the service command, so we can do stuff like sudo service nginx restart.

$ sudo curl https://raw.github.com/JasonGiedymin/nginx-init-ubuntu/master/nginx | cat >> /etc/init.d/nginx
$ sudo chmod +x /etc/init.d/nginx
$ sudo /usr/sbin/update-rc.d -f nginx defaults

We’ll also replace the default nginx configuration with a leaner one. Download the simple config file and replace the current one (backing up first, of course).

$ curl https://raw.github.com/alexpearce/templates/master/nginx.conf | cat >> nginx.conf
$ sudo mv /opt/nginx/conf/nginx.conf /opt/nginx/conf/nginx.conf.old # back up
$ sudo mv ./nginx.conf /opt/nginx/conf

Notice that this new configuration file includes files from /opt/nginx/sites-available. This is similar to how many people set up Apache with virtual hosts. For each site we want (generally represented as a (sub)domain or set of (sub)domains) we create a file inside sites-enabled.

If you installed nginx to a different directory, such as /etc/nginx, modify the nginx init.d and nginx.conf file to point to the right places. Check everything’s working by restarting nginx.

$ sudo service nginx restart
* Stopping Nginx Server...
  ...done.
* Starting Nginx Server...
  ...done.

We can double-check nginx is working by visiting our VPS IP.

A successful nginx installation.

Great! Now we have all we need to run Ruby apps. With something like Rails we need one one more thing though: a database. The version of PostgreSQL in the Ubuntu repository is a little outdated, so we’ll install it from an up-to-date PPA.

$ sudo add-apt-repository ppa:pitti/postgresql
$ sudo apt-get update
$ sudo install postgresql libpq-dev

libpq-dev is a dependency of the pg gem, which Rails will need to talk to the database. Now that PostgreSQL is installed, we’ll change the root password and set up a test user.

$ pqsql

postgres=# \password
Enter new password:
...
postgres=# create user test with password 'secret';
postgres=# create database test_production owner test;
postgres=# \q

$

Good, that’s everything for now. Let’s try getting a Rails app up and running!

Testing

We’ll get a fresh Rails app going as the deploying user, so make sure you’re logged in as deploy.

$ su - deploy

We want each app to have its own set of gems. Using Bundler, we can do this easily with a Gemfile.

$ mkdir testapp
$ cd testapp
$ echo -e ’source :rubygems\n\ngem "rails"' >> Gemfile

We now have a Gemfile which Bundler can work with. We now install the bundler gem, install the stuff in our Gemfile to vendor/bundle (which will act as our app-specific gemset) and create the Rails test app.

$ gem install bundler
$ rbenv rehash
$ bundle install --path vendor/bundle
$ bundle exec rails new . -d postgresql

Choose Y to overwrite our Gemfile with the Rails template. Now, install the rest of the Rails dependencies.

$ bundle install
...
Your bundle is complete! It was installed into ./vendor/bundle

To make sure Rails can talk to the database, edit database.yml with the test PostgreSQL user and database details.

$ nano config/database.yml
...
production:
  adapter: postgresql
  encoding: unicode
  database: test_production
  pool: 5
  host: localhost
  username: test
  password: secret
...

The host: localhost is particularly important (and not in the default Rails database.yml); my app wouldn’t work without it.

Now we just need to tell nginx where our Rails app is. As I mentioned earlier, we’ve set up nginx so that each site has its own config file inside nginx/sites-enabled. I’ve uploaded a test app config file on GitHub. We just download this to this nginx directory and edit it so that it points to the right place.

$ curl https://raw.github.com/alexpearce/templates/master/nginx.server.conf | cat >> testapp
$ nano testapp
... # edit the config file so that it points to our app
$ sudo mv testapp /opt/nginx/sites-enabled/
$ sudo service nginx restart

Visiting the VPS IP, it works!

A successful Rails installation.

Excellent. We’ve set up a VPS from scratch to serve Ruby applications, installing rbenv, Passenger, nginx, and PostgreSQL. If you’d like to test the Rails app a little further, delete index.html in the public directory and scaffold some models. Create, update, and delete a few records to make sure the database is OK.

# Inside the ~deploy/testapp directory
$ rm public/index.html
$ rails g scaffold post title body:text
$ touch tmp/restart.txt # this tells passenger to restart the app

That’s all for now, next we’ll go over deploying from a local machine to the VPS with Capistrano.