My preferred hosting platform is Ubuntu Linux. I’ve posted guides for Ubuntu 9.10 and Ubuntu 10.04 previously. Since then, I’ve starting using Ruby Version Manager. RVM has drastically changed how I provision a server. So it is time to share a new approach.
This recipe is based on my own experience and preferences. You will find many other opinions. I highly recommend you study each step and understand what packages and functionality you are enabling. When it comes time to deploy to production, you want a minimal configuration. My hope is that this can help others get a sensible Rails server up and running quickly.
The steps are done assuming a new clean install of Ubuntu 10.10 Server. It should be nearly identical on Ubuntu Desktop. I’ll use
apt-* commands so you can do everything from the command line. Use synaptic, dpkg, or any other package tool if you wish.
By default, Ubuntu Server is extremely minimal. The only package I typically select in the Software selection page is the OpenSSH Server. Otherwise, I install everything else explicitly. I recommend using RVM to manage Ruby and gems. There are a few packages you will need to have in place before you can proceed with RVM.
Install the development package that includes GCC. You should also install Git source control tools. Even if you aren’t using git for your own code, you should use it for RVM. Finally, add a few other tools that will be useful:
sudo apt-get install build-essential git curl vim python-software-properties
Ruby Version Manager
The Ruby Version Manager project has radically altered how developers manage Ruby and gems. It allows the Ruby environment to be fine tuned per application. My previous post explained how to setup RVM on Mac OSX for development. For an Ubuntu server, the approach is a little different. RVM has a specific install method for system wide installation. This is the recommended way to setup a dedicated Rails server.
System wide install is designed to be used by multiple applications or users. It is installed into
/etc/rvm. It also creates an rvm group for managing permissions. In short, it is an ideal method for managing Ruby on an application server.
To install, RVM you need to be in a root shell. This command will download a bootstrap script that will do a git clone of RVM into
sudo -s bash < <( curl -L http://bit.ly/rvm-install-system-wide )
The output of the script will explain what RVM has done. In particular, it will offer a recommended list of dependencies that should be installed. It will also explain how to modify the bash environment to properly load RVM.
First, install the remaining recommended dependencies. A few were already installed in previous steps. The following have not yet been installed:
Next, update the bash environment for any users that should load RVM:
Add the following to the end of the file:
Additionally, fix up statements that embed a return statement in a bash if clause:
Change these to:
In particular, edit the following:
Repeat for all users that will run Ruby via RVM. This includes editing the root user as well in
/root/.bashrc. Now that the bash environment has been configured, log out and log back in. Verify RVM is correctly configured by running the following command:
$ rvm --version rvm 1.1.6 by Wayne E. Seguin (firstname.lastname@example.org) [http://rvm.beginrescueend.com/]
Ruby and Gems
With the base server provisioned, Ruby and gems can be added. RVM provides tools for easy management of multiple versions of Ruby. Gems can also be managed by RVM. However, applications should use Bundler to freeze gems into the source tree for deployment. You will need to have a version of Ruby installed along with a very basic set of gems. Most other gems should be packaged (even native extensions). The gem list will be very short.
For Rails 3, the current default is to use Ruby 1.9.2 MRI. Another common choice is to use Phusion’s Ruby Enterprise Edition (REE) patches. Either is a good choice for a standard Rails server. If you are integrating Java code, JRuby is a good choice. Other Ruby implementations like Rubinius are a little more experimental right now.
I prefer using Ruby 1.9.2 over the other options. Use RVM to install Ruby 1.9.2 along with the minimal default Ruby gems. Also, set Ruby 1.9.2 as the default Ruby interpreter and install the Bundler gem:
sudo rvm install ruby-1.9.2 sudo rvm --default use 1.9.2 sudo -s rvm use 1.9.2 gem install bundler
Verify the install of Ruby 1.9.2 by activating it:
$ rvm use ruby-1.9.2 $ rvm info ruby-1.9.2-p136: system: uname: "Linux railshost 2.6.35-22-server #35-Ubuntu SMP Sat Oct 16 22:02:33 UTC 2010 x86_64 GNU/Linux" bash: "/bin/bash => GNU bash, version 4.1.5(1)-release (x86_64-pc-linux-gnu)" zsh: " => not installed" rvm: version: "rvm 1.1.9 by Wayne E. Seguin (email@example.com) [http://rvm.beginrescueend.com/]" ruby: interpreter: "ruby" version: "1.9.2p136" date: "2010-12-25" platform: "x86_64-linux" patchlevel: "2010-12-25 revision 30365" full_version: "ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-linux]" homes: gem: "/usr/local/rvm/gems/ruby-1.9.2-p136" ruby: "/usr/local/rvm/rubies/ruby-1.9.2-p136" binaries: ruby: "/usr/local/rvm/rubies/ruby-1.9.2-p136/bin/ruby" irb: "/usr/local/rvm/rubies/ruby-1.9.2-p136/bin/irb" gem: "/usr/local/rvm/rubies/ruby-1.9.2-p136/bin/gem" rake: "/usr/local/rvm/gems/ruby-1.9.2-p136/bin/rake" environment: PATH: "/usr/local/rvm/gems/ruby-1.9.2-p136/bin:/usr/local/rvm/gems/ruby-1.9.2-p136@global/bin:/usr/local/rvm/rubies/ruby-1.9.2-p136/bin:/usr/local/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin:/usr/local/rvm/bin" GEM_HOME: "/usr/local/rvm/gems/ruby-1.9.2-p136" GEM_PATH: "/usr/local/rvm/gems/ruby-1.9.2-p136:/usr/local/rvm/gems/ruby-1.9.2-p136@global" MY_RUBY_HOME: "/usr/local/rvm/rubies/ruby-1.9.2-p136" IRBRC: "/usr/local/rvm/rubies/ruby-1.9.2-p136/.irbrc" RUBYOPT: "" gemset: "" $ ruby --version ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-linux] $ gem list *** LOCAL GEMS *** rake (0.8.7) rubygems-update (1.4.1)
Ruby 1.9.2 is available and has the absolute minimal gems installed.
Nginx Web Server
I’m a recent convert to nginx. I have used Apache exclusively for years for Rails and other web projects. Currently nginx is rapidly gaining favor in the Rails community due to the simpler configuration and strong performance. Apache is still a valid choice but you should take a hard look at nginx for Rails. Phusion Passenger supports both equally well. The primary difference between Apache and nginx is how requests are managed. Apache uses either a thread or process based approach. Nginx uses asynchronous events.
On Ubuntu, the version of nginx in the default repositories is somewhat dated. There is a PPA maintained by the nginx project. It is easy to add custom repositories in Ubuntu. You can either add entries directly to
/etc/apt/sources.list or create individual list files in
/etc/apt/sources.list.d. The second approach seems like a cleaner method to me.
There is one other caveat. Unlike Apache, nginx does not support loadable modules. Therefore, the server must be rebuilt to add a different module. If you plan to use Passenger, skip to that section below. The Passenger installer will download and build nginx for you.
If you want to install nginx and control the modules, the following creates an apt source entry for the nginx ppa. There are three flavors of install:
nginx-full. The difference is in which modules are added. You will have to judge for yourself if you need any of the extra modules. Run
apt-cache show [pkgname] to list the modules included. For this tutorial, I’ll use the light package:
$ sudo -s # echo "deb http://ppa.launchpad.net/nginx/stable/ubuntu maverick main" >> /etc/apt/sources.list.d/nginx.list # apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C300EE8C # apt-get install nginx-light
After install, the nginx web server should be up and running. Enter your hostname or IP address in your browser and you should see:
Besides a web server, Rails needs an applications server. Passenger is a good choice for most applications. It provides good performance and reliability. It is also relatively easy to use. Additionally, there is now a standalone mode for Passenger that you can use during development.
Unicorn is another popular choice for resource intensive or high volume sites. Unicorn leverages the Linux kernel itself for managing workers. Unicorn is being used on some well known sites like GitHub and Twitter. If you are managing lots of fast clients at high bandwidth, Unicorn is likely a better choice. Unicorn is an application server. A proxy must be used to route HTTP requests.
Since this is a more general recipe, let’s use Passenger. It is a module you can use with both Apache and nginx. One Ruby interpreter is used for all applications. If you need to run more than one version of Ruby, you will need to use a proxy and multiple instances of application servers.
First, install the Passenger 3 gem:
$ sudo -s # gem install passenger
The next step is to build the module for the web server. If you are using nginx and are not using it for other applications, the easiest thing to do is to let Passenger download and build it for you. Pick option 1 when prompted by the installer. Pick a directory for the install. I recommend using
$ sudo -s # passenger-install-nginx-module Automatically download and install Nginx? Enter your choice (1 or 2) or press Ctrl-C to abort: 1 Where do you want to install Nginx to? Please specify a prefix directory [/opt/nginx]: /opt/nginx
The installer will create a valid configuration in
/opt/nginx/conf/nginx.conf that references the current RVM Ruby and Gems. Next, configure nginx with a startup script:
sudo mkdir /opt/nginx/init.d sudo wget --no-check-certificate http://github.com/ascarter/nginx-ubuntu-rvm/raw/master/nginx -O /opt/nginx/init.d/nginx sudo chmod +x /opt/nginx/init.d/nginx sudo ln -s /opt/nginx/init.d/nginx /etc/init.d/nginx sudo /etc/init.d/nginx start sudo /etc/init.d/nginx status sudo /etc/init.d/nginx stop sudo /usr/sbin/update-rc.d -f nginx defaults
PostgreSQL Database Server
I prefer PostgreSQL for my database server. I like the feature set and have found it reliable, robust, and high performance.
For Ubuntu 10.10, PostgreSQL 9 was released too late to make it in the official repositories. Martin Pitt manages a PPA that can be used to get the latest stable release. If you want to use 8.4, you can use the one included in the primary repository.
Add the PostgreSQL PPA and install PostgreSQL 9:
sudo add-apt-repository ppa:pitti/postgresql sudo apt-get update sudo apt-get install postgresql libpq-dev
The default PostgreSQL install has one role defined for
postgres. Ideally, you should create a role for each application to keep in order to implement least required privileges. To create a role and a database for an application, use the following commands:
sudo -u postgres createuser -D -A -P myuser sudo -u postgres createdb -O myuser mydb sudo -u myuser psql mydb
Creating a Rails Application
Let’s create a quick test application that can be used to validate the server. On your development machine, set your RVM environment to Ruby 1.9.2 with the Rails 3 gems installed. Create the application:
rails new myapp --database=postgresql cd myapp git init git add * git commit -a -m "Initial import" bundle install git add .gitignore git commit -a -m "Add .gitignore"
myapp/config/database.yml file to match the user and password. In this example, the database server is localhost. If you are using a shared database, replace as appropriate.
Packaging Gems with Bundler
The next step is to package up the gems for the application. Bundler is the preferred tool currently for packing gems into the application for deployment. Be sure to add any gems you need to the Gemfile. Once the gem file is complete, package the gems and add to the repository:
bundle check bundle install bundle package git add vendor/cache git commit -a -m "Vendor gems"
Deploying an Application With Capistrano
Capistrano is an excellent tool for deploying Ruby applications. Even if you use a provisioning tool like Chef, Capistrano is likely the solution you will use to push new versions of your application.
First, enable Capistrano in the gem file by uncommenting the gem:
Next, install capistrano:
bundle check bundle install bundle package git add vendor/cache git commit -a -m "Enable capistrano"
Capify the application. This will create a default
deploy.rb file that will be used to configure the deployment.
myapp/config/deploy.rb file to look like the following. Edit the user and paths to match your system:
For easy of deployment, I recommend adding public keys to the SSH authorized keys for the application user for all users that will perform deployments. This avoids having to share a password for the application user. Also, create a root directory to store your applications. This directory should be owned by either your application user or group writable for a user that the application user is a member of.
The final step is to test a deployment. Push your repository to a location accessible by your Rails server. Then provision the application directory and deploy:
cap deploy:setup cap deploy
The app will now be in the application directory. Initialize the database if necessary. On the rails host, run the following:
sudo -u myuser -s cd /opt/mycompany/myapp/current RAILS_ENV=production rake db:schema:load RAILS_ENV=production rake db:seed RAILS_ENV=production rake db:migrate
The final task is to configure nginx for your application. Edit
/opt/nginx/conf/nginx.conf to add your application:
Restart nginx and your application should be available.
Ubuntu Server provides the foundation for creating efficient Rails application hosts. RVM and bundler help separate the Ruby configuration from the base server tasks. Try creating a Rails host in a virtual machine to tune the approach to fit your own requirements.