http://www.developer.com/

Back to article

Take Rails to the Cloud: Deploying a Rails Application to Heroku


January 20, 2010

Heroku, a commercial cloud computing platform for deploying Rails and other Rack-based web applications, uses PostgreSQL database services and provides migration tools to quickly move MySQL (or other relational databases) to its platform. Heroku is deployed on Amazon EC2 and uses Amazon Elastic Block Store (EBS) persistent storage volumes.

Heroku is very simple to use and it abstracts developers from most Rails deployment issues. Its use of Amazon AWS infrastructure services also makes Heroku a good choice for deploying web applications that use other Amazon services, because you pay no bandwidth fees inside specific Amazon zones.

This article explains how to use the Heroku deployment platform by walking you through the development and deployment of a simple database-backed Rails application.

These topics are based on my personal use cases for Heroku deployments and experiments. Hopefully, you will find them useful for your own work and deployments.

Developing and Deploying a Rails Application to Heroku

The examples in this article work towards implementing a simple Rails app: Note Taker with Search. The code download contains all the examples, and you should extract them and work along with me through every example.

Handling User Authentication

To keep the code for the examples short and easy to follow, I will use an easy user-authentication account setup that works well for web apps serving only a few users. Assume that every example contains a text file db/accounts.txt that has a line for each authenticated user, for example:

Mark Watson:mark:mypassword873459435
Otis Watson:otis:otispassword4235244


This shortcut will keep the examples short and make it easier for you to understand the Heroku-specific parts.

Creating the First Rails Project

First, you'll create a Rails project using PostgreSQL and a scaffold that you will customize later. Here is the code to install a few gems you will need and then to generate the Rails application files:

gem install pg
gem install postgres
gem install texticle  # to use PostgreSQL's built in index and search functions
rails --database=postgresql note_taker_pg


You probably should modify the generated database.yml file. I like to fetch any passwords as environment variables. (Later, I'll show you how to register environment variables with Heroku so you don't need to embed them in your source or configuration files). My database.yml file looks like this:

development:
  adapter: postgresql
  encoding: unicode
  database: note_taker_development
  username: postgres
  password: "#{ENV['POSTGRESQL_PASSWORD']}"


You do need to set a PostgreSQL password for Heroku, so leave POSTGRESQL_PASSWORD undefined in your environment unless your local development environment has a password set. I do password protect my postgres PostgresSQL user, so I set it on my laptop. Later, when you use remote MongoDB or CouchDB servers, I'll show you how to set up local config variables on Heroku to specify host names, accounts, and passwords for these remote services.

Now, make sure that the generated web app runs:

script/server


Tip: As a Rails developer, you probably should define bash shell aliases for script/server, script/console, etc. I use rs and rc.

What If You Want to Develop Using MySQL?

You can use an existing Rails application developed with MySQL for this example, as well as develop locally on your laptop using MySQL or SQLite. When you perform the heroku db:push operation, the trick is to move a local database to your Heroku application. If you have your database.yml file configured to use a different database than PostgreSQL, the Heroku system will automatically convert your application to use PostgreSQL. (Later, I'll show you how to set up the database after you finish writing and testing the application locally.)

If you use non-standard SQL features from a different database server, Heroku's documentation has some troubleshooting tips. I have moved three MySQL-based web applications to Heroku and have never had problems.

Finishing the Test Web Application

You can now delete the default HTML page and generate the scaffolds you need:

rm public/index.html
script/generate scaffold Note title:string content:string user_id:integer
script/generate scaffold User login:string password:string


This example uses the scaffold for the User class as a login page. Before you set up the routes for this application and customize the controllers and views, let's finish the models using Aaron Patterson's neat texticle ruby gem to integrate PostgreSQL's text indexing and search features into an ActiveRecord model.

You just need to add a few lines to the Note and User model class definitions to specify that (1) you will use PostgreSQL indexing and search on the Note class fields title and content, and that (2) a user has many notes:

class Note < ActiveRecord::Base
  index do  # define which fields will be indexed for text search
    title
    content
  end 
end
class User < ActiveRecord::Base
  has_many :notes # each user can have many notes
end


I need to modify the User class migration file to read in valid accounts from db/accounts.txt:

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string :login
      t.string :password
      t.timestamps
    end
    User.transaction do
      File.new("db/accounts.txt", "r").readlines.each do |line|
        login, passwd = line.strip.split(':')
        User.new(:login => login, :password => passwd).save
      end
    end
  end
 
  def self.down
    drop_table :users
  end
end


Note that you did not have to modify the generated Note class migration file.

If you want to test this Rails application on your PC before pushing it to Heroku, run a rake task to create the database and another to create the tables on your PC:

rake db:create
rake db:migrate


Tip: When I develop on Rails, I almost always keep a Rails console open. Here is a snippet that checks that two rows are added to the table users during the migration:
script/console
>> u = User.find(1)
=> #<User id: 1, login: "mark", password: "mypassword873459435", 
created_at: "2009-11-13 22:17:31", updated_at: "2009-11-13 22:17:31"> >> u.notes << Note.new(:title => "test title", :content => "test content") => [#<Note id: 1, title: "test title", content: "test content", user_id: 1,
created_at: "2009-11-1322:43:26", updated_at: "2009-11-13 22:43:26">]


If you are not experienced with ActiveRecord, then the last statement may be confusing. Just know that you do not need to set the user ID in the new note object, or to save the new note object; ActiveRecord takes care of both of these operations for you. Also, if you ever remove a note from a user object, ActiveRecord will manage removing the appropriate row from the database table notes.

You are done with the database modeling, but you can now modify the generated controllers and views.

Modifying the Scaffold Views

For this exercise, you will modify the scaffold views only to show a search term input field and search results on any page the user sees after logging in (i.e., any page generated with data from the NotesController).

Start by editing the file app/views/layouts/notes.html.erb by adding:

<div style="float:right;">
  <form>
    <strong>Search:</strong> <input type="text" name="search" value="<%=params[:search]%>"/>
  </form>
  <%= @results %>
 
</div>


Figure 1 shows a screenshot of the scaffold web app with an added search field.


Figure 1. Scaffold Web App with an Added Search Field

Now, add a bit of code to the NotesController class:

  before_filter :do_search # needs to go after "before_filter :require_login"
 
  def do_search
    @results = ''
    if params[:search]
      notes = Note.search(params[:search])
      @results += "<h4>Results:</h4>" if notes.length>0
      notes.each {|note| @results += "<a href=\"/notes/#{note.id}\">#{note.title}</a><br/>"}
     end
  end


With these simple changes to the notes controller and the default notes layout, every scaffold page becomes search enabled.

Creating the Database on Heroku

You can use another command line tool to define this environment variable in the context of your web application deployed on Heroku. Define a blank password because your Heroku database is available only in your application's runtime context, so a password won't be required to make a database connection:

$ heroku config:add POSTGRESQL_PASSWORD=
Adding config vars:
  POSTGRESQL_PASSWORD => 
Restarting app...done.
I can now create my database:
$ heroku rake db:migrate
(in /disk1/home/slugs/86008_449e6c2_e27f/mnt)
==  CreateNotes: migrating ====================================================
-- create_table(:notes)
   -> 0.0141s
==  CreateNotes: migrated (0.0142s) ===========================================
 
==  CreateUsers: migrating ====================================================
-- create_table(:users)
   -> 0.0112s
==  CreateUsers: migrated (0.0338s) ===========================================

Heroku offers many timesaving features for web application developers. One of them is opening your system's default web browser with the URL of your deployed application:

$ heroku open Opening http://note-taker-pg.heroku.com/


Development Work Flow Using Heroku

When I deploy to one of my own servers or to an Amazon EC2 instance, I do a lot of work using a combination of Capistrano and Chef to set up a custom deployment scheme. Using Heroku eliminates all of this setup work. Although I like to host my git repositories on one of my own servers, you can use the repository created for your web application on Heroku (See "Sidebar 1. Using Git"). As an example, if I wanted to create a new working copy of my application (say I needed to make a change while at a friend's house), I could treat my Heroku git repository as any other remote repository. Here is how I would check out a new copy:

git clone git@heroku.com:note-taker-pg.git


When you check out a new clone of your project, it will not be set up for Heroku. So, you would need to reuse the heroku command line program. This requirement is why I usually just use my original copy for development.

During development, you edit and test against the local copy of your web application. You can at anytime see which files you have modified, commit changes, and redeploy your application on Heroku in a few seconds:

git status
# On branch master
# Changed but not updated:
#   (use "git add ..." to update what will be committed)
#   (use "git checkout -- ..." to discard changes in working directory)
#
#   modified:   app/controllers/notes_controller.rb
#
# Untracked files:
#   (use "git add ..." to include in what will be committed)
#
#   config/database-local.yml
no changes added to commit (use "git add" and/or "git commit -a")
markws-macbook:note_taker_pg markw$ git commit -a -m "added comments to notes controller"
[master 3639dce] added comments to notes controller
 1 files changed, 1 insertions(+), 0 deletions(-)
markws-macbook:note_taker_pg markw$ git push heroku master
Counting objects: 9, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 580 bytes, done.
Total 5 (delta 2), reused 0 (delta 0)
 
-----< Heroku receiving push
-----< Rails app detected
       Compiled slug size is 92K
-----< Launching..... done
       http://note-taker-pg.heroku.com deployed to Heroku
 
To git@heroku.com:note-taker-pg.git
   449e6c2..3639dce  master -> master


The default deployment at Heroku is free and uses one "dyno" compute unit. It also provides 5MB of storage for your database. A dyno is similar to a mongrel process in that it handles one request at a time. Just as you can run a cluster of mongrels on your own server, you can use the Heroku web interface to increase or decrease the number of dynos allocated to your web application.

When I am done with a deployed test application, I use Heroku's web interface to delete it. This frees up resources, a polite gesture when using the free deployment option.

Code Download

For Further Reading

About the Author

Mark Watson is a consultant living in the mountains of Central Arizona with his wife Carol and a very feisty Meyers Parrot. He specializes in web applications, text mining, and artificial intelligence development. He is the author of 16 books and writes both a technology blog and an artificial intelligence blog.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date