Deploying Rails Applications
on Mac OS X Leopard
In Developing Rails Applications on Mac OS X Leopard, we created a web application using the latest Rails features with Xcode 3.0. In Customizing Rails Applications on Mac OS X Leopard, we learned how to customize views, create web forms, and add AJAX and iPhone support. In this third article, we'll finish up by deploying the Rails application on Mac OS X Leopard Server.
Traditionally deployment has been painful because it involves getting all the required software installed, configuring various moving parts, remembering to copy the right files at the right time, and so on. Leopard Server changes all that. In addition to Ruby and Rails, Leopard Server comes pre-installed with everything we need to deploy and run a production Rails application: Apache 2.2, mod_proxy_balancer, MySQL, Mongrel, Capistrano, and a few other unique goodies. Indeed, Leopard Server raises the bar when it comes to ease of Rails deployment.
In a short amount of time we've built a functional expense-tracking application. Developing on Leopard gave us a big productivity boost with its built-in support for Ruby and Rails. Deploying our Rails application should be no different. So now we're going to transition over to Leopard Server for another boost.
Updating Ruby Gems
The first thing we need to do is update the necessary Ruby gems to their latest versions. Log in to your production server running Leopard Server and type these commands:
sudo gem update --system sudo gem install rails sudo gem update rake sudo gem update capistrano sudo gem update mongrel
In addition to updating the RubyGems system, Rails, and Rake as we did on our development machine, these commands also update two additional gems we'll need for deployment. Capistrano is an automated deployment utility and Mongrel is a popular web server for running Rails applications. Running all these commands may take a while. When you've finished, double-check that you're using the latest version of Rails (2.0.2 for this article) by typing
rails -v
Setting Up the Production Database
We developed this application using a SQLite3 database. In production, we'll switch over to a MySQL database. MySQL 5.0.45 is pre-installed on Leopard Server, and configuring our application to use MySQL in production couldn't be easier.
By default, all Rails applications have three runtime environments:
development, test, and production. When our application runs in the
production
environment, it changes its personality: it runs
faster, logging is less verbose, users see friendlier error pages, and so on.
Equally important, in production the application uses the database that's
configured in the production
section of the
config/database.yml
file.
On your development machine, update the production
section of the
config/database.yml
file inside your Rails application directory to use MySQL as follows:
production: adapter: mysql database: expenses_production username: root password: your-mysql-root-password_here socket: /var/mysql/mysql.sock
Then, back on the server, open the Server Admin app and start the MySQL
Service, if it's not already running. Next, open a Terminal and create the
expenses_production
database by typing
mysqladmin -u root -p create expenses_production
Now, before we run our application in production we'll need to make sure the
schema in the expenses_production
MySQL database matches the
schema in our SQLite3 development database. Here's where database migrations
really shine.
Remember, our migration files in the db/migrate directory consist of database-agnostic Ruby code that represents our schema. That means we can run the migrations against any database supported by Rails. We just need to make sure to apply all the migrations to our production database when we deploy the application. We'll take care of that a little later.
Putting the Code Under Version Control
Before we go any further, let's get our application code safely tucked away in a Subversion version control repository. Mac OS X 10.5 Leopard ships with Subversion 1.4.4 pre-installed, so it's easy to start getting the benefits of version control early.
First, we need to create a Subversion repository. We'll put it in the
/Users/Shared/svnrepo
directory on our server, but it can live
anywhere. We just need it to be accessible from our production server. To
create the repository, log in to your server and type
svnadmin create /Users/Shared/svnrepo
Next, we need to import our Rails application into the repository. Back on
your development machine, make sure you're in the expenses
directory containing the Rails application. Then, substituting in your server name, type
svn import -m 'Initial import' . \ svn+ssh://your-server-name/Users/Shared/svnrepo/expenses
At this point, we have a copy of our Rails application in the Subversion repository on the server. That means we can check out a copy of the application onto any machine that has secure shell (SSH) access to our server. To do that on your development machine, for example, you'd type
svn co svn+ssh://your-server-name/Users/Shared/svnrepo/expenses
We'll use the version of our application in the Subversion repository as the "gold master" that gets deployed to our production server.
Creating a Deployment Recipe
Deploying our application to the production server is something we plan to do often. As new features are developed and bugs are fixed, we'll want to re-deploy the application without a lot of fuss. We don't want to have to log in and out of servers, copy files around, and run a checklist of tasks manually every time we deploy the application. Instead, we want to automate the deployment process so that it's consistent and repeatable.
Leopard includes the Capistrano automated deployment utility which makes deploying Rails applications a breeze. All the deployment ingredients and steps are neatly spelled out in a deployment recipe file. Every time you want to deploy a new version of your Rails application, you simply type one command.
Back on your development machine, change directory to the expenses
directory containing the Rails application
and type
capify .
This command creates a template Capistrano recipe in the config/deploy.rb
file. Clear out the file and add the following sections incrementally as we go.
First, Capistrano needs the deployment ingredients. Add the following variables and settings in the config/deploy.rb
recipe file:
set :application, "expenses" set :repository, "svn+ssh://your-server-name/Users/Shared/svnrepo/#{application}" set :deploy_to, "/Library/WebServer/#{application}" set :deploy_via, :export set :scm_username, "your-subversion-username" set :scm_password, "your-subversion-password" ssh_options[:forward_agent] = true
The application
variable is just an arbitrary name for our application. The repository
variable is where the
master copy of our application source code lives. In this case, Capistrano will use the
svn+ssh
protocol to access the Subversion repository we set up previously. (Capistrano supports other version control systems, as well.) The deploy_to
variable is the name of the directory on the production server where the application will get deployed. You'll need to set the scm_username
and scm_password
variables for your environment. The last line
may be important depending on how your SSH keys are set up. Basically, it lets
the SSH connection to the Subversion repository use your SSH agent, if one is
running, so you don't have to enter passwords repeatedly.
With this part of the recipe complete, Capistrano knows how to check out the Rails application from our Subversion repository and deploy it into the
/Library/WebServer
directory on the production server. The one missing ingredient is the name (or IP address) of the production server.
Add the following to your recipe file, substituting your server name or IP address:
role :app, "your-server-name.com" role :web, "your-server-name.com" role :db, "your-server-name.com", :primary => true
A role is simply a named group of servers that Capistrano uses to specialize the behavior of certain deployment tasks. In this case, we're deploying to a single server and the role distinction doesn't matter. However, roles become important when you start to scale up with multiple servers, as we'll see later.
That takes care of the deployment ingredients. Next, Capistrano needs to know
the deployment steps, or tasks. It has a number of default tasks
including start
, stop
, and restart
.
We'll define our own versions of those tasks to start, stop, and restart our
Rails application.
We'll be running our Rails application using Mongrel, which is a
web server pre-installed on Leopard for running Rails applications.
Mongrel has a mongrel_rails
command for starting and stopping
Mongrel processes. To streamline deployment, Leopard Server includes an
enhanced version of the mongrel_rails
command called
mongrel_rails_persist
. It creates a launchd
plist
file to run a Mongrel process persistently across reboots. It also registers
the Mongrel process with Bonjour so that clients looking for
httpd
services can find the Mongrel process.
Add the following Mongrel-specific variables to the recipe file:
set :mongrel_cmd, "/usr/bin/mongrel_rails_persist" set :mongrel_ports, 3000..3003 set :user, "administrator" set :group, "admin"
These aren't special variables; we've just defined them here to keep the rest
of the recipe tidy. The first two variables define the full path to the mongrel_rails_persist
command and a range of ports (3000-3003) for each Mongrel process. In this case, we'll be starting four Mongrel processes. We'll use the user
and group
variables simply to set the user and group ownership for each process.
Next, add the following Capistrano tasks to your recipe file:
namespace :deploy do desc "Start Mongrels processes and add them to launchd." task :start, :roles => :app do mongrel_ports.each do |port| sudo "#{mongrel_cmd} start -p #{port} -e production \ --user #{user} --group #{group} -c #{current_path}" end end desc "Stop Mongrels processes and remove them from launchd." task :stop, :roles => :app do mongrel_ports.each do |port| sudo "#{mongrel_cmd} stop -p #{port}" end end desc "Restart Mongrel processes" task :restart, :roles => :app do stop start end end
The start
and stop
tasks loop through each port
number (3000-3003). Each time through the loop, the sudo
method
is used to run the mongrel_rails_persist
command, which starts or
stops a Mongrel process on the appropriate port. Notice that the start
task starts our Rails application in the production
runtime environment using the -e
option.
It's important to note that the sudo
method runs the
mongrel_rails_persist
command on the production server(s)
listed in the app
role via the sudo
command.
Capistrano uses the secure shell (SSH) to execute these command remotely. You
can pass an :as
option to the sudo
method to specify
who the command is executed as. Alternatively, you can use the
run
method to run the command on the production server without
sudo privileges.
The restart
task simply runs the
start
and stop
tasks in succession.
Deploying for the First Time
Capistrano assumes we'll want our application deployed to a consistent directory structure on all the production machines. It's the directory we specified in the deploy_to
variable, which in this case
is /Library/WebServer/expenses. We haven't created that directory yet. Rather than logging into our server and creating the directory manually, from this point forward we'll use Capistrano to run tasks remotely for us.
On your development machine, type
cap deploy:setup
In the output you'll see Capistrano log in to your production server using SSH and create the following directory structure:
/Library/WebServer/expenses/ /Library/WebServer/expenses/releases /Library/WebServer/expenses/shared /Library/WebServer/expenses/shared/log /Library/WebServer/expenses/shared/system /Library/WebServer/expenses/shared/pids
Now we're ready to deploy our Rails application for the first time. This is somewhat of a special case. Typically our application will already be running on the production server, and we'll just redeploy it. The first time we deploy, however, the application isn't running. Moreover, we haven't applied the database migrations to our production database.
For a first-time deployment, run a special "cold" deploy task by typing
cap deploy:cold
In the output you'll see that this task logs in to your production server via
SSH, checks out the application code from Subversion into a new directory
under the /Library/WebServer/expenses/releases
directory, and
then creates a /Library/WebServer/expenses/current
symlink that
points to the new release. This current
symlink is used as the
root directory of the currently-active release.
The task then runs all the migrations so that our production database schema
matches our development database schema. Finally it runs the
start
task we wrote earlier to fire up all four Mongrel processes
for the first time.
At this point your application is running on the production server. Assuming you're logged in to your production server, point Safari at http://127.0.0.1:3000/events and you should see the expenses application.
Configuring the Web Server
We're able to access our application by talking directly to a Mongrel process via a port. Now we need to configure the Web Service to transparently proxy all incoming requests to our pack of Mongrel processes.
Follow these steps to configure the Web Service on Leopard Server:
-
Open the Web Service. On the production server, open the Server Admin app and connect to the server. Then click the triangle to the left of the server. From the list of services, select Web.
-
Configure the Web Service. Click Sites and select the website in the list. Then click Proxy below the websites list. Select the Enable Reverse Proxy checkbox and verify that the Proxy Path field is set to "/". This will proxy all URLs within the website to the balancer members.
-
Add Balancer Members. Each balancer member corresponds to a Mongrel process, running on either the local host or other hosts. Click the Add (+) button below the Balancer Members list. In the Server URL pop-up menu, you should see four Mongrel processes listed with unique URLs as shown in Figure 14.
Figure 14: Leopard Server Admin: Adding Balancer Members
These are the Mongrel processes that were started when we cold deployed the application. Server Admin was able to find the processes because the
mongrel_rails_persist
command registered them with Bonjour.Select a balancer member and set its Load Factor to 25 to distribute the load equally among balancer members. Leave the Route field blank unless you have a specific reason to enter a value.
Repeat this step until all four balancer members have been added. Figure 15 shows the final configuration.
Figure 15: Leopard Server Admin: Configuration for a dedicated website
-
Save the Configuration. Click Save and then click Start Web Service, if it's not already running.
-
Confirm Access. Point Safari at http://127.0.0.1/events to confirm that the URL is proxied to the Rails application.
Using this configuration, our website (virtual host) is dedicated to the Rails
application. Alternatively, we can arrange things so that the website is
shared with the Rails application. Say, for example, we want the Rails
application to be mounted under the /expenses
URI.
To do that, simply change the mongrel_rails_persist
command in
the start
task of the Capistrano recipe to include a
--prefix /expenses
option.
sudo "#{mongrel_cmd} start -p #{port} -e production \ --prefix /expenses --user #{user} --group #{group} -c #{current_path}"
Then, in the Proxy Path field of the Web Service configuration, enter the
prefix with both a leading and trailing slash. In our example this would be
/expenses/
. When you add Balance Members, the Server URLs
shown in the pop-up menu will include /expenses
as shown in
Figure 16.

Figure 16: Leopard Server Admin: Configuration for a shared website
Then to access the shared application, point Safari at http://127.0.0.1/expenses/events.
Deploying New Releases
After the first deployment, things get even easier. When you have a new release you want to deploy, first make sure all your code is checked in to the Subversion repository. Then, on your development machine, redeploy by typing
cap deploy
That command checks the latest version of your Rails application out into a
timestamped subdirectory of the releases
directory, flips the
current
symlink to the new release, and restarts all the Mongrel
processes so that they pick up the new code.
If there's a problem with the new release, you can easily roll back to the previous release by typing
cap deploy:rollback
Rolling back is painless (and fast!) because it just updates the
current
symlink to point to the previous release and restarts all the Mongrel processes.
Scaling Up
In this example we deployed to a single Leopard Server. As your application becomes more popular, eventually you may want to start building a cluster of Leopard Servers, each with their own speciality. Capistrano makes it very easy to add new servers to your deployment process.
First, simply tell Capistrano about each server, and what role each plays. Here's an example:
role :app, "your-app1-server.com", "your-app2-server.com", "your-app3-server.com" role :web, "your-web1-server.com", "your-web2-server.com" role :db, "your-db-server.com", :primary => true
Next, set up each new server by typing
cap deploy:setup
Then deploy code as usual by typing
cap deploy
When you run a Capistrano task such as deploy
or
deploy:rollback
, it's run in parallel (and atomically) on all
machines that are assigned to the app
role in the Capistrano
recipe file. Think about that: As the number of machines and processes in your
deployment environment varies, your deployment procedure remains constant and consistent. It's just one command.
Conclusion
Ruby on Rails is a highly-productive web application framework. It scales from the simplest expense tracking application to full-featured applications with respectable numbers of users. Leopard is there to help every step of the way. You can get a useful Rails application up and running quickly on your PowerBook, then seamlessly deploy it to a production-ready Xserve (or cluster of Xserves) running Leopard Server.
For More Information
This article has introduced you to Ruby on Rails on Leopard. The following references will help you dig deeper:
- Official Rails web site
- Complete Rails API documentation
- Official Capistrano web site
- The book Agile Web Development with Rails is an excellent guide to learning Rails and Advanced Rails Recipes includes practical ways to solve advanced problems.
- The Pragmatic Studio offers three-day intro and advanced Ruby and Rails workshops.
- Big Nerd Ranch also offers a five-day Ruby on Rails Bootcamp.
Updated: 2008-09-29