Posts tagged Rails

With the release of Ubuntu 12.04 LTS the time has come - albeit late - for an updated guide on rolling your own Rails production server with a long term supported Ubuntu. This is an updated guide based on an earlier version for Ubuntu 11.10. Most steps are the same, but I've gone through the guide in a fresh virtual machine to make sure everything still works as expected.

The setup

I'm assuming that you already have installed Ubuntu Server with a non-root user (with sudo rights) and that SSH is setup (e.g. like this.) Use sudo, do not use root, because this is a requirement for rvm. You should setup your hosts file for your server. For example, my server has these settings:

# /etc/hosts
127.0.0.1   localhost
12.34.56.78 server.web-l.nl

# /etc/hostname
server.web-l.nl

This should prevent any messages from apache saying that it could not determine a fully qualified domain name (fqdn).

Installing Apache

We'll be installing apache using apt-get. You'll need sudo to do this. While we are installing apache, we might just as well include some packages needed later on.

sudo apt-get install apache2 curl git build-essential zlibc zlib1g-dev zlib1g libcurl4-openssl-dev libssl-dev libopenssl-ruby apache2-prefork-dev libapr1-dev libaprutil1-dev libreadline6 libreadline6-dev

Now redirect your web browser to your servers IP. It should show a welcome page saying: "It works!". Good, you've got your basic webserver working!

Installing MySQL

Up next is MySQL, our database server. Install MySQL via apt-get, the installer will ask you for a database root password, make sure to pick a strong password.

sudo apt-get install mysql-server

Let's also create a mysql user and database for your rails app. I'm naming both the database and the user myrailsapp, but you are free to pick any name you feel appropriate. Make sure the replace the password (correcthorsebatterystaple) with something more secure. You can open the mysql console using:

mysql -u root -p

Run the following commands to create a user and database:

create database myrailsapp;
grant all privileges on myrailsapp.* to 'myrailsapp'@'localhost' identified by 'correcthorsebatterystaple'; 
flush privileges;
exit;

You can verify the account by running mysql -u myrailsapp -p

Installing RVM and Ruby

We'll get to setting up passenger later, but lets first install RVM and a recent ruby. We'll use the curl command provided on the RVM website. Note: This is the first and only time you will use sudo to do something with RVM. After this, a user just needs to be in the RVM group to use rvm. If, for any reason, sudo is needed to do something with rvm, you have to use rvmsudo instead of sudo.

*Note: * The installation script changed location recently, the new command is:

curl -L https://get.rvm.io | sudo bash -s stable

This should successfully install RVM in a multi-user install. The next step is to add yourself to the RVM user group. Let's also add a deploy user with RVM access while we're doing this. Add yourself to the deploy group for convenience.
Also set the permissions on the /var/www folder. With these settings every user in the deploy group can modify files in the /var/www folder. Then, log out of your terminal/shell and log back in. (This will update your group permissions)

sudo adduser deploy
sudo adduser deploy rvm
sudo adduser <yourusername> deploy
sudo adduser <yourusername> rvm
sudo chown -R deploy:deploy /var/www
sudo chmod g+w /var/www

Time to install a Ruby. You can check the list of available rubies using rvm list known. I have chosen to install ruby 1.9.3-p194, which is the latest stable version at the time of writing. Compilation can take some time, so get a caffeinated beverage of your choice while your server is busy compiling. Then, set the new ruby to be your default ruby and install bundler. (We'll need bundler later)

RVM, Bundler, Rubygems and management

Basically, there are several good ways to manage our gems with respect to Passenger. There also is a wrong way, which is to install all gems in the same rvm gemset on the same server. Good options (in my opinion):

  1. Use a single server for each application. This gives the best separation and redundancy, but it can cost you, especially if you have a lot of low traffic apps.
  2. Using a separate rvm gemset for each application. There are some really good instructions on how to do this in this blog post by Darcy.
  3. Use Bundlers --deployment option to install all gems into the shared/bundle directory of your app. This is what we'll use in this guide, because it does not require any extra setup (unlike option 2), while it provides full separation of gems for your apps

You ask why I've installed RVM even if I'm going with option 3??? Well, this is because rvm makes it easier to install and maintain your desired ruby on Ubuntu. The apt packages for ruby are usually a bit behind.

Installing Phusion Passenger

Start by installing passenger. Then install passenger-install-apache2-module using rvmsudo. The module installer is very helpful and will give you directions if anything is missing.

gem install passenger
rvmsudo passenger-install-apache2-module

In the end, the installer will show how to configure the apache configuration files in order to use passenger. Instead of editing the configuration files directly, we'll place 2 files (passenger.load and passenger.conf) in /etc/apache2/mods-available, so that we can use a2enmod and a2dismod to active/deactivate the module. Use sudo to edit these files. The contents below are indicative, use what the passenger installer will return to you. (most likely, the version numbers will change with time)

#/etc/apache2/mods-available/passenger.load
LoadModule passenger_module /usr/local/rvm/gems/ruby-1.9.3-p194/gems/passenger-3.0.15/ext/apache2/mod_passenger.so

#/etc/apache2/mods-available/passenger.conf
PassengerRoot /usr/local/rvm/gems/ruby-1.9.3-p194/gems/passenger-3.0.15
PassengerRuby /usr/local/rvm/wrappers/ruby-1.9.3-p194/ruby

Activate the passenger module and restart Apache.

sudo a2enmod passenger
sudo service apache2 restart

And voila, you now have Apache running with Passenger 3.

Deploying

In a later guide I'll show how to deploy to your server using Capistrano. Capistrano takes some time to setup, but it is very useful because future deployments can then be performed completely automatic with a single command.

In this section we'll deploy a basic rails app by hand.

  1. Move the default page from /var/www to /var/www/default.
  2. Create and deploy a new rails app.
  3. Configure an Apache Virtual host to access your rails app.

1. Moving the default page

We will move de default apache page into a separate directory to prevent unauthorized access to the directories of your Rails app. Failing to do this would make the internals of your application accessible via http://ip.address/railsapp. This could, for example, provide unauthorized access to database passwords.

mkdir /var/www/default
mv /var/www/index.html /var/www/default/index.html

Now edit the default apache vhost to point to this directory.

# /etc/apache2/sites-available/default
...
DocumentRoot /var/www/default
...
<Directory /var/www/default/>
...
  Options FollowSymLinks # Remove Indexes

2. Deploying a new rails app

We'll scaffold a todo rails app at the server to show the deployment of a rails app. To show how bundler installs gems in production, I'll use a separate gemset that is not accessible by Passenger. This simulates the situation in which you develop on a separate computer. We also run bundle install once to generate a Gemfile.lock that bundler will use when deploying. We'll also add therubyracer gem to our Gemfile, because we need a javascript interpreter later.

Note: Bundler is not really setup to both develop and deploy on the same machine. Although the steps below are informative in showing how to deploy an app, I recommend you develop on a separate machine.

rvm use 1.9.3@rails3 --create
gem install rails
cd /var/www
rails new myrailsapp --skip-bundle -d mysql
cd myrailsapp
echo "gem 'therubyracer'" >> Gemfile
bundle install
rails generate scaffold todo name:string finished:boolean
rvm gemset use global

In the new rails app we have to setup the MySQL database.

# /var/www/myrailsapp/config/database.yml
production:
  adapter: mysql2
  encoding: utf8
  reconnect: false
  database: myrailsapp
  username: myrailsapp
  password: correcthorsebatterystaple

Now change the ownership to the deploy user:

sudo chown -R deploy:deploy /var/www/myrailsapp

We'll run bundler as the deploy user to simulate the behavior we'll expect with capistrano in the next guide. Log in as deploy with ssh before continuing. rake assets:precompile requires a javascript interpreter, good thing you added therubyracer to the Gemfile. Touching tmp/restart.txt restarts passenger.

rvm use 1.9.3 --default
cd /var/www/myrailsapp
bundle install --deployment --without development test
bundle exec rake db:migrate RAILS_ENV=production
bundle exec rake assets:precompile
touch /var/www/myrailsapp/tmp/restart.txt

3. Configure a vhost

The easiest way to check if your rails site is working, is to redirect the default virtual host to your rails app. However, if you have a domain name you could also point the DNS settings to your server and setup a vhost to serve that domain. For simplicity we'll turn off the default vhost and redirect all traffic to our railsapp vhost. Note: the vhost needs to direct to the public folder of your app, everything else is handled by Passenger automagically.

# /etc/apache2/sites-available/myrailsapp
<VirtualHost _default_:80>
  # ServerName www.example.com # Commented out for default
  DocumentRoot /var/www/myrailsapp/public 
  # be sure to point to public!!
  <Directory /var/www/myrailsapp/public/>
    AllowOverride all
    Options -MultiViews
  </Directory>
</VirtualHost>

Now, activate the vhost by running sudo a2ensite myrailsapp, then deactivate the default vhost by running sudo a2dissite default, and then reload the apache configuration using sudo service apache2 reload and send your browser to http://<serverip>/todos. If all went well, you should see your working Rails application.

Continue to part 2 of this guide to learn how to deploy on this setup with capistrano.

Please feel free to notify me iIf anything is not behaving as expected.

Want your own weblog? This on is available on GitHub.

Back to index

BCrypt is a way to hash passwords with a given work-factor. I'm not going into the details here, but it comes down to 2 main properties

  1. BCrypt is difficult to implement on GPU's, which limits the speed in which a dedicated attacker can create hashes. (Unlike SHA or MD5 hashes, which were optimized for speed)
  2. BCrypt allows you to specify a work factor so that your hashing difficulty can grow with time.

One problem I found when running my tests in RSpec is that BCrypt is so good at its job that it has quite some impact on my tests. Every user that gets created takes out some time to generate a bcrypt hash and in the end this slows down each test by a few 100ms.

The solution is to reduce the work-factor, of course. However, implementing this in my user-model doesn't seem right, why would I adapt my code for tests? It only creates another entry for bugs or security issues. Simple solution: override bcrypts default work-factor in the test environment:

# config/environment/test.rb
require 'bcrypt'
Abc::Application.configure do
    # ...
    BCrypt::Engine::DEFAULT_COST = 4
    # ...
end
Back to index

I was working on a new project and usually I use the twitter_bootstrap_form_for gem to get good looking forms with Bootstrap and Rails. However, this time I was working with the new Bootstrap 2.0 -- which is great! -- and twitter_bootstrap_for_form currently lacks support for Bootstrap 2.0. So, I set off to find another solution, I've long wanted to do more with the formtastic gem, so combining this with bootstrap was actually kind of a good thing.

As it turned out, there is a formtastic-bootstrap gem that can do most of the heavy lifting for you. Unfortunately, it does not play nice with bootstrap 2.0, or with formtastic 2.1+ for that matter. I had to use a fork of the formtastic-bootstrap gem and force the version of formtastic to 2.1 (vs 2.2, which is the latest release). Just to make things easy, a short summary of the work involved.

I'm assuming you already have bootstrap 2.0 installed. If not, either add it to vendor/stylesheets and require it in the application.css or install it via the twitter-bootstrap-rails gem. Installation instructions are given on that page.

Getting Bootstrap, Rails and Formtastic to play nice

Add formtastic and formtastic-bootstrap to your gemfile. I'm using the version patched by cgunther for bootstrap 2.0 and formtastic 2.1 compatibility.

# Gemfile
gem 'formtastic', '~> 2.1.1'
gem 'formtastic-bootstrap', '~> 1.1.2',
  :git => 'git://github.com/cgunther/formtastic-bootstrap.git',
  :branch => 'bootstrap2-rails3-2-formtastic-2-1'

After running a bundle install, run rails generate formtastic:install to install formtastic. Now, edit the formtastic initializer to give it a new FormHelper.builder.

# config/initializers/formtastic.rb
Formtastic::Helpers::FormHelper.builder = FormtasticBootstrap::FormBuilder

And you're done!

Back to index
Dennis

Sign-in using GitHub

Dennis | about 1 year ago | Rails Omniauth

I want a painless way for visitors of my website to post comments, without going through the hassle of registering for an account. OmniAuth is a gem that allows us to do just that. In this tutorial I'll show how to enable users to sign-in/up using github and OAuth.

The setup

We're going to configure omniauth to use github for a users credentials. We do however create a user for the authenticated user, which will contain the information we obtain from GitHub. For one, this makes sure that our app doesn't crash and burn when GitHub is offline.

We're going to assume that you already have an authentication scheme working. With a sign_in(user) method that performs all the work related to sessions, etc. because I just want to show the implementation of omniauth itself.

OmniAuth has several strategies. You can install these separately in your Gemfile. Here we'll use the omniauth-github gem. Look at the OmniAuth wiki for more strategies.

# Gemfile
gem 'omniauth-github'

# Bash
bundle install

With omniauth-github installed, we have to setup the authentication. OmniAuth is provided as a rack middleware application and we include it in our app using an initializer. Create a new file in the config/initializers path and give it a descriptive name (e.g. omniauth.rb). The initializer does the setup of the authentication strategy for github. It needs a github-key and github-secret. You can obtain these at GitHub. For development purposes you can set the application url to http://localhost:3000 and the callback url to http://localhost:3000/auth/github/callback.

# config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :github, APP_CONFIG[:github_id], APP_CONFIG[:github_secret]
end

As you see above, I store my variables in a global has called APP_CONFIG. You can also just put the key and secret right in the initializer. However, whatever you do, do not make the secret public, they don't call it a secret for nothing.

Believe it or not, but with this minimal configuration, the first part of the authentication is already working. Fire up your rails server and go to /auth/github. The application will forward you to GitHub, where it will ask you if you want to allow it to have access.

The next step from github will be to make a call back to your application. If everything went right, it should call to /auth/github/callback, if a user rejects your application, it will call /auth/failure. Let's catch those calls and do something with it.

When we reach /auth/failure, we could for example want to redirect the user to the root path and display an error message saying that the authentication failed.

A successful callback will provide you with the users information in request.env["omniauth.auth"], You could use pry or the rails logger to inspect this object.

# config/routes.rb
match "/auth/:provider/callback" => "sessions#create_from_github"
match "/auth/failure" => "sessions#failure_from_github"

# app/controllers/sessions_controller.rb
def create_from_github
   # Rails.logger.debug request.env["omniauth.auth"]
end

def failure_from_github
  flash[:error] = "Error logging in with GitHub. #{params[:message]}"
  redirect_to root_path
end

What we really want to do with the information, however, is two things.

  1. Check if the user is new. If so, create a new User model for this user.
  2. Find the User model that belongs to this user and log him in.

We can do this using the uid that is provided by GitHub in the returned omniauth.hash. Lets add this column to our user model.

rails generate migration add_github_uid_to_users github_uid:string

Now, in our SessionsController, we can check if a user with this ID already exists by searching the user database for that GitHub UID. If it doesn't find one, we can create a new user using the information provided by GitHub.

# SessionsController
def create_from_github
  omniauth = request.env["omniauth.auth"]
  @user = User.find_by_github_uid(omniauth["uid"]) || User.create_from_omniauth(omniauth)
  # Hook into your own authentication system!
  sign_in @user 
  # This would normally be configured to return to the previous path 
  redirect root_path, :notice => "Welcome, #{@user.name}"  
end

Creating the user is done using the createfromomniauth method:

#user.rb
def self.create_from_omniauth(omniauth)
  User.new.tap do |user|
    user.github_uid = omniauth["uid"]
    user.name = omniauth["info"]["nickname"]
    user.email = omniauth["info"]["email"]
    user.save!
  end
end

Now, when a user tries to login using omniauth, your call will create a user if necessary, log him user in and redirect him to the root path. Great, isn't it? But, how does a user login using omniauth? Simple, just redirect him to /auth/github and omniauth will take care of everything else.

Notes:

I found that OAuth is picky about domain names. So, www.web-l.nl and web-l.nl are two different worlds. So, if you setup your application to use example.com, make sure that all requests at www.example.com get redirected to the preferred domain name before making the OAuth call.

In my case, I use Apache and mod_rewrite. In the virtualhost block I rewrite the url to use the root domain name.

RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
RewriteRule ^(.*)$ http://example.com$1 [R=301,L]
Back to index

Rails 3.2.0 has been released and it spots some very welcome new features! Among these is the new store in Active Record. The Active Record store is a convenient way to store key-value pairs in a model, without having to define extra columns in the database.

The post by @dhh gives a nice example of it's use (shown below), but I wanted to go into the details a bit further. Another good explanation is given on the Year of Moo blog, but they define their store column as a string, which will limit the store to only a few key-value pairs, so make sure to change this to a text column when following their tutorial.

class User < ActiveRecord::Base
  store :settings, accessors: [ :color, :homepage ]
end
u = User.new(color: 'black', homepage: '37signals.com')
u.color                          # Accessor stored attribute
u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor

The Active Record Store explained

The session store in Active Record provides a way to add additional attributes to a model. Unlike the normal attributes, which are each stored in a separate column in the database, session store attributes are serialized as a JSON object. The JSON object is then stored as a normal attribute in a single column in the database.

You could define something similar very easily, but using store is a convenience method that can automatically generate accessor methods for your store attributes. Next to this, you can always add extra attributes by accessing the store object itself directly.

So, you ask, why use the Active Record Store when we already have normal attributes in Active Record? Well, sometimes you don't want to go the whole way for a simple key-value store. The cons? You can't search the database for these columns, so attributes in the AR Store should only be used within the scope of a single record.

An example setup:

In this example we're going to use the AR Store to store preferences of a user. In this case, the color of the homepage and the number of recent items to be shown. Shown below, the basic user class:

class User < ActiveRecord:Base
    # other columns: first_name, last_name
    store :preferences, accessors: [ :homepage_color, :n_recent_items]
end

For this to work, we'll also need a database column names preferences to store these attributes. Make sure to define this as a text column, so that their is enough space for all your attributes!

# migration
class AddPreferencesToUsers < ActiveRecord::Migration
  def change
    add_column :users, :preferences, :text
  end
end

Now, migrate your database (rake db:migrate), and fire up the console (rails console).

user = User.new
user.first_name = "John" # this is a normal attribute
user.last_name  = "Doe"  # this is a normal attribute
user.homepage_color = "#3333FF" # this is an AR store attribute
user.n_recent_items = 12        # this is an AR store attribute
user.preferences[:homepage_width] = 920 # Directly edit the hash, even for new attributes
user.save

Now, in the database, your record looks like this

first_name: "John"
last_name:  "Doe"
preferences: "{ homepage_color: '#3333FF', n_recent_items: 12, homepage_width: 920 }"

And that's all about the Active Record Store. A flexible way to add light attributes to your models.

Back to index

Happy New Year!

The other day I was making a website where a user could rearrange categories by dragging them away from their old location to their new location. The categories were placed in <div> tags. Because it took me some time to figure out how to save the updated records in my rails app, I decided to write this experience into a little tutorial.

First off, make sure that yo have JQuery and JQuery UI up and running in your rails app. If you want a minimal JQuery UI installation, go to the custom download page and select only the sortable module. (all other dependencies will be selected automatically).

First aspect of our app, the user interface. I wrote mine in haml. See below that we have a div#sortable that contains div.category elements. Each category element has an id that is equal to the id in the database for this category. (This is important, as we'll see later)

#sortable
  - @categories.each do |cat|
    .category{:id => cat.id}
      = cat.name

To make the list of categories sortable, we add a little javascript. The code below makes the elements within the div#sortable draggable. When the order of the list changes, the update callback is triggered. There, we ask sortable to return to us an ordered array which contains the IDs of the categories. We then put the array in a hash, which is then sent to the application using an ajax post call.

$(window).load(function() {
    $("#sortable").sortable({
      update: function(event, ui){
        var itm_arr = $("#sortable").sortable('toArray');
        var pobj = {categories: itm_arr};
        $.post("/categories/reorder", pobj);
      }
    });
});

That is as far as the user end goes. Now, what happens in the Rails application? First you'll need a route for the ajax call. We'll direct the call to the reorder method of the categories_controller.

resources :categories do
  collection do
    post 'reorder'
  end
end

Then, in our categories_controller.rb we'll update the sequence of our categories. What happens here? Once you know that every category has a sequence field (and integer). It is acutally quite simple:
1. We get the category ID's from the params hash
2. We go over the hash, setting the sequence to the value of n. n starts at 0 and increases with every category.
3. We save and return an empty JSON object together with a 200 OK code.

class CategoriesController < ApplicationController

  # ...

  def reorder
    @category_ids = params[:categories]
    n = 0
    ActiveRecord::Base.transaction do
      @category_ids.each do |id|
        category = Category.find(id)
        category.sequence = n
        n += 1
        category.save
      end
    end
    render :json => {}
  end
end

I had some trouble with sequence values of nil after creating a new category object. I solved this by validating the presence of a sequence number, and by setting the next sequence number on a new category when none was present.

class Category < ActiveRecord::Base
  attr_accessible :name

  has_many :items, :dependent => :destroy

  validates :sequence, :presence => true
  validates :name, :presence => true

  default_scope order(:sequence)
  before_validation :set_sequence

  private

    def set_sequence
      if self.sequence.nil?
        last_sequence = TariffCategory.order(:sequence).last.sequence
        self.sequence = 1 + (last_sequence.nil? ? 0 : last_sequence)
      end
    end
end

And thats it! You now have a persistent way to rearrange categories (or any other div that represent an ActiveRecord model in your user interface)

Back to index

I was working on a search form and I wanted to use the twitterbootstrapform_for gem. My form was not based on some persisted model, but was just a ruby class. Unfortunately, form_for expects some basic rails abilities from my model. The solution? Bring some goodness from ActiveModel into your model.

Here's an example, in case you run into the same problem:

class NonPModel
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend  ActiveModel::Naming

  # Required to use this model in a form builder
  def persisted?
    false
  end
end
Back to index

In this guide we'll build on Part 1 to setup an environment where you can deploy your app via Capistrano! Capistrano automates the deployment process, which makes it incredibly efficient to deploy your app and keep it updated.

Let's start by adding Capistrano in our gem file. For this guide I'm assuming that your app is already hosted on GitHub.

group :development do
  gem 'capistrano'
  ...
end

How does Capistrano work?

Capistrano manages your app on the server using three directories: releases, which houses timestamped directories with each deployment of your app, shared, which houses files shared between deployments, and current, which links to the most recent release.

In essence, Capistrano runs two commands when you use it to deploy your app: deploy:update_code and deploy:symlink. The first command checks out a fresh copy of your app from git(hub) into a new release directory. The second command updates the symlink of the current directory to the latest release and the shared directories.

All of this works because Capistrano SSHs into your server and runs the appropriate commands. To make things go smooth make sure that you can login to your server with the deploy user using SSH keys.

Basic setup

Lets continue setting up a basic configuration: navigate to the root of your app (on your development machine) and run capify .. This will create two new files: a Capfile in the root of your app and deploy.rb in the config directory. The Capfile calls deploy.rb, which is the file where we can setup our deployment options. My basic setup is listed below.

# config/deploy.rb
$:.unshift(File.expand_path('./lib', ENV['rvm_path'])) # Add RVM's lib directory to the load path.
require 'rvm/capistrano'  # Add RVM integration
require 'bundler/capistrano'  # Add Bundler integration
load 'deploy/assets'  # only for rails 3.1 apps, this makes sure our assets are precompiled.

set :application, "web-l.nl"
role :web, "12.1.12.11"  # Your HTTP server, Apache/etc
role :app, "12.1.12.11"  # This may be the same as your `Web` server
role :db,  "12.1.12.11", :primary => true  # This is where Rails migrations will run

set :scm, :git
set :repository, "git@github.com:HectorMalot/WebL.git"
set :branch, "master"

set :user, "deploy"
set :deploy_to, "/var/www/#{application}"
set :deploy_via, :remote_cache
set :use_sudo, false

Now, go to the root of your rails app (on your development setup) and run:

cap deploy:setup #setup capistrano directories at server
cap deploy:check #check if all dependencies are met

If all went well, you can now run cap deploy, and Capistrano will place your app on the server.

cap deploy  # Deploy your app
cap deploy:migrate  # Run any migrations if needed

All that is left to do is configuring Apache and Passenger to load the app. For this we'll create a new virtual host. SSH into your server and go to /etc/apache2/sites-available. Create a new file (sudo vi webl.nl) for the virtual host of your app. Mine is listed below, note that the document root is pointing to current/public.

# /etc/apache2/sites-available/web-l.nl
<VirtualHost *:80>
        ServerAdmin webmaster@web-l.nl
        ServerName web-l.nl
        ServerAlias www.web-l.nl
        DocumentRoot /var/www/web-l.nl/current/public
        <Directory /var/www/web-l.nl/current/public>
                Allow from all
                Options -MultiViews
        </Directory>
</VirtualHost>

With the virtual host setup correctly you only have to activate the vhost and reload apache to get your site working.

sudo a2ensite web-l.nl
sudo service apache2 reload

Now, visit your domain and your app should be there, up and running.

This was a bit of a short guide to get the basics working. My current script has a lot more functionality, but for those tips I'd like to redirect you to the following excellent tutorials:

Back to index

Update:

There is an updated version of this guide for ubuntu 12.04 LTS

Original Guide

When I started developing in Rails I found that setting up a Rails development environment is fairly trivial. I had expected no different from setting up a Rails production server. However, having setup a Rails production server over the past few days has thought me that setting up a production environment is a lot more complex than a development environment.

Add to this that although the development process is well documented for new programmers, the production people assume that you are experienced and know what you are doing. Otherwise, why would you be running a production environment in the first place? But, there has to be a first time for everything and with this (and upcoming) guides I hope to make the Rails production life a bit easier.

In this guide I'll show how to setup a Rails production server. In part 2 of this guide I show how to deploy to this server using Capistrano, which will make your deployment process a lot easier. During this setup we'll keep that goal in mind.

The setup

I'm assuming that you already have installed Ubuntu Server with a non-root user (with sudo rights) and that SSH is setup (e.g. like this.) Use sudo, do not use root, because this is a requirement for rvm. You should setup your hosts file for your server. For example, my server has these settings:

# /etc/hosts
127.0.0.1   localhost
12.34.56.78 server.web-l.nl

# /etc/hostname
server.web-l.nl

This should prevent any messages from apache saying that it could not determine a fully qualified domain name (fqdn).

Installing Apache

We'll be installing apache using apt-get. You'll need sudo to do this. While we are installing apache, we might just as well include some packages needed later on.

sudo apt-get install apache2 curl git build-essential zlibc zlib1g-dev zlib1g libcurl4-openssl-dev libssl-dev libopenssl-ruby apache2-prefork-dev libapr1-dev libaprutil1-dev libreadline6 libreadline6-dev

Now redirect your web browser to your servers IP. It should show a welcome page saying: "It works!". Good!

Installing MySQL

Up next is MySQL, our database server. Install MySQL via apt-get, the installer will ask you for a database root password, make sure to pick a strong password.

sudo apt-get install mysql-server

Let's also create a mysql user for your rails app. I'm naming both the database and the user myrailsapp, but you are free to pick any name you feel appropriate. Make sure the replace the password (correcthorsebatterystaple) with something more secure.

create database myrailsapp;
grant all privileges on myrailsapp.* to 'myrailsapp'@'localhost' identified by 'correcthorsebatterystaple'; 
flush privileges;

Installing RVM and Ruby

We'll get to setting up passenger later, but lets first install RVM and a recent ruby. We'll use the curl command provided on the RVM website. Note: This is the first and only time you will use sudo to do something with RVM. After this, a user just needs to be in the RVM group to use rvm. If, for any reason, sudo is needed to do something with rvm, you have to use rvmsudo instead of sudo.

*Note: * The installation script changed location recently, the new command is:

sudo bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer )

This should successfully install RVM in a multi-user install. The next step is to add yourself to the RVM user group. Let's also add a deploy user with RVM access while we're doing this. Add yourself to the deploy group for convenience.
Also set the permissions on the /var/www folder. With these settings every user in the deploy group can modify files in the /var/www folder. Then, log out of your terminal/shell and log back in. (This will update your group permissions)

sudo adduser deploy
sudo adduser deploy rvm
sudo adduser <yourusername> deploy
sudo adduser <yourusername> rvm
sudo chown -R deploy:deploy /var/www
sudo chmod g+w /var/www

Time to install Ruby. You can check the list of available rubies using rvm list known. I have chosen to install ruby 1.9.2-p290, but you could (should?) go for 1.9.3 once a stable release is available. Compilation can take some time, so get a caffeinated beverage of your choice while your server is busy compiling. Then, set the new ruby to be your default ruby and install bundler. (We'll need bundler later)

rvm install 1.9.2 
rvm --default use 1.9.2
gem install bundler

I've had some issues to get openssl and the readline library working with ruby on the beta of Ubuntu Oneiric. One solution is to use rvm pkg install openssl and rvm pkg install readline before installing ruby (you can reinstall ruby using: rvm reinstall 1.9.2. More info can be found at the rvm website.

RVM, Bundler, Rubygems and management

Basically, there are several good ways to manage our gems with respect to Passenger. There also is a wrong way, which is to install all gems in the same rvm gemset on the same server. Good options (in my opinion):

  1. Use a single server for each application. This gives the best separation and redundancy, but it can cost you, especially if you have a lot of low traffic apps.
  2. Using a separate rvm gemset for each application. There are some really good instructions on how to do this in this blog post by Darcy.
  3. Use Bundlers --deployment option to install all gems into the shared/bundle directory of your app. This is what we'll use in this guide, because it does not require any extra setup (unlike option 2).

You ask why I've installed RVM even if I'm going with option 3? Well, this is because rvm makes it easier to install and maintain your desired ruby on Ubuntu. The apt packages for ruby are usually a bit behind.

Installing Passenger

Start by installing passenger. Then install passenger-install-apache2-module using rvmsudo. The module installer is very helpful and will give you directions if anything is missing.

gem install passenger
rvmsudo passenger-install-apache2-module

In the end, the installer will show how to configure the apache configuration files in order to use passenger. Instead of editing the configuration files directly, we'll place 2 files (passenger.load and passenger.conf) in /etc/apache2/mods-available, so that we can use a2enmod and a2dismod to active/deactivate the module. Use sudo to edit these files. The contents below are indicative, use what the passenger installer will return to you. (most likely, the version numbers will change with time)

#/etc/apache2/mods-available/passenger.load
LoadModule passenger_module /usr/rvm/gems/ruby-1.9.2-p290/gems/passenger-3.0.9/ext/apache2/mod_passenger.so

#/etc/apache2/mods-available/passenger.conf
PassengerRoot /usr/local/rvm/gems/ruby-1.9.2-p290/gems/passenger-3.0.9
PassengerRuby /usr/local/rvm/wrappers/ruby-1.9.2-p290/ruby

Activate the passenger module and restart Apache.

sudo a2enmod passenger
sudo service apache2 restart

And voil, you now have Apache running with Passenger 3.

Deploying

In a later guide I'll show how to deploy to your server using Capistrano. Capistrano takes some time to setup, but it is very useful because future deployments can then be performed completely automatic with a single command.

In this section we'll deploy a basic rails app by hand.

  1. Move the default page from /var/www to /var/www/default.
  2. Create and deploy a new rails app.
  3. Configure an Apache Virtual host to access your rails app.

1. Moving the default page

We will move de default apache page into a separate directory to prevent unauthorized access to the directories of your Rails app. Failing to do this would make the internals of your application accessible via http://ip.address/railsapp. This could, for example, provide unauthorized access to database passwords.

mkdir /var/www/default
mv /var/www/index.html /var/www/default/index.html

Now edit the default apache vhost to point to this directory.

# /etc/apache2/sites-available/default
...
DocumentRoot /var/www/default
...
<Directory /var/www/default/>
...
  Options FollowSymLinks # Remove Indexes

2. Deploying a new rails app

We'll scaffold a todo rails app at the server to show the deployment of a rails app. To show how bundler installs gems in production, I'll use a separate gemset that is not accessible by Passenger. This simulates the situation in which you develop on a separate computer. We also run bundle install once to generate a Gemfile.lock that bundler will use when deploying. We'll also add therubyracer gem to our Gemfile, because we need a javascript interpreter later.

Note: Bundler is not really setup to both develop and deploy on the same machine. Although the steps below are informative in showing how to deploy an app, I recommend you develop on a separate machine.

rvm use 1.9.2-p290@rails31 --create
gem install rails
cd /var/www
rails new myrailsapp --skip-bundle -d mysql
cd myrailsapp
rails generate scaffold todo name:string finished:boolean
echo "gem 'therubyracer'" >> Gemfile
bundle install
rvm gemset use global

In the new rails app we have to setup the MySQL database.

# /var/www/myrailsapp/config/database.yml
production:
  adapter: mysql2
  encoding: utf8
  reconnect: false
  database: myrailsapp
  username: myrailsapp
  password: correcthorsebatterystaple

We'll run bundler as the deploy user to simulate the behavior we'll expect with capistrano in the next guide. Log in as deploy with ssh before continuing. rake assets:precompile requires a javascript interpreter, good thing you added therubyracer to the Gemfile. Touching tmp/restart.txt restarts passenger.

rvm use 1.9.2-p290 --default
bundle install --deployment --without development test
bundle exec rake db:migrate RAILS_ENV=production
bundle exec rake assets:precompile
touch /var/www/myrailsapp/tmp/restart.txt

3. Configure a vhost

The easiest way to check if your rails site is working, is to redirect the default virtual host to your rails app. However, if you have a domain name you could also point the DNS settings to your server and setup a vhost to serve that domain. For simplicity we'll turn off the default vhost and redirect all traffic to our railsapp vhost. Note: the vhost needs to direct to the public folder of your app, everything else is handled by Passenger automagically.

# /etc/apache2/sites-available/myrailsapp
<VirtualHost _default_:80>
  # ServerName www.example.com # Commented out for default
  DocumentRoot /var/www/myrailsapp/public # be sure to point to public
  <Directory /var/www/myrailsapp/public/>
    AllowOverride all
    Options -MultiViews
  </Directory>
</VirtualHost>

Now, activate the vhost by running sudo a2ensite myrailsapp, then deactivate the default vhost by running sudo a2dissite default, and then reload the apache configuration using sudo service apache2 reload and send your browser to http://<serverip>/todos. If all went well, you should see your working Rails application.

Continue to part 2 of this guide to learn how to deploy on this setup with capistrano.

Back to index
Dennis

First Post!

Dennis | over 1 year ago | Rails

I finally succeeded. It took some time, especially since I had to learn Capistrano on the go, but the blog is online and kicking. It'll move to a better domain sooner or later.

Lessons for today:

  1. Capistrano is actually quite useful, once you get the hang of it.
  2. I'll have to re-setup a VPS to get some stuff in the back end worked out. That whole convention over configuration thing didn't work out as well as I had hoped. Read: it failed me every single step of the way. I have a strong suspicion that this is related to our particular setup and not to the nice folks behind Rails.
  3. The Asset Pipeline is both a beautiful idea and extremely annoying if something works in development and fails in production, or if you are using css files that reference images.
Back to index