The super easy guide to deploying a Merb app on Ubuntu with Passenger

Passenger and Merb are a very nice combo. It’s pretty simple to get an app deployed under a virtual host, and my mind is put to ease when I think that my development environment mirrors my production environment.

With the addition of Merb’s bundling, the junk you need to have installed on a system you’re deploying to has also been cut down dramatically. Taking it even a step further, you can use VMWare to mirror your production machines relatively closely, so deploying Merb (or Rails :P) apps to a cheapo VPS has become much more easy and enjoyable.

This article goes into detail about getting a fresh Ubuntu 8 machine up and running with Passenger and Merb.

Bootstrapping the System

First you need to install some stuff on the machine. I’ve managed to whittle it down to what’s needed on a brand new Ubuntu 8 machine over at SliceHost.

I’ll usually just scp or sftp the file to the machine and run it from my home directory:

bootstrap-system.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/bin/bash
 
TS=`date +%s`
 
sudo apt-get update
sudo apt-get upgrade
 
sudo apt-get -y install \
build-essential ruby ruby1.8-dev ri rdoc \
libopenssl-ruby git-core subversion \
screen zip libpq-dev sqlite3 libsqlite3-dev libmysqlclient15-dev \
apache2 apache2-prefork-dev 
 
mkdir -p ~/src
cd ~/src
 
wget http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz
tar xzvf rubygems-1.3.1.tgz
cd rubygems-1.3.1
sudo ruby setup.rb
sudo ln -nfs /usr/bin/gem1.8 /usr/bin/gem
sudo gem update --system
 
sudo gem install passenger --no-ri --no-rdoc
sudo gem install thor --no-ri --no-rdoc
sudo passenger-install-apache2-module
 
cat > /tmp/passenger_conf_$TS <<END
LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-2.0.6/ext/apache2/mod_passenger.so
PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-2.0.6
PassengerRuby /usr/bin/ruby1.8
END
 
sudo cp /tmp/passenger_conf_$TS /etc/apache2/conf.d/passenger

Passenger/Apache Vhost Config

Once installed, Passenger is enabled for the root of any vhost.

We’ll create a sample vhost to use for development. Put this file in /etc/apache2/sites-available.

/etc/apache2/sites-available/merb-dev.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<VirtualHost *:80>
  ServerName merb-dev.local
  DocumentRoot "/data/merb-dev/current/public"
  CustomLog "/data/merb-dev/shared/log/access.log"  common
  ErrorLog "/data/merb-dev/shared/log/error.log" 
  RackEnv production
 
  <Directory "/data/merb-dev/current/public">
    Options FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all 
  </Directory>
</VirtualHost>

And then link it into the sites-enabled directory:

  sudo ln -s /etc/apache2/sites-available/merb-dev.conf \
     /etc/apache2/sites-enabled/merb-dev.conf
  sudo apache2ctl restart

Set up your Merb app

Next, you need to setup your Merb app and commit some files. Merb has a pretty cool bundling mechanism which allows you to bundle up all of your dependencies within the source tree, which frees you from system-wide gems messing anything up.

I won’t go into many of the details, but there are a couple good articles to read before proceeding. One is Bundling Merb with your application. Carl Lerche also did a Merb Bundling screencast which is also very useful.

dependencies.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# dependencies are generated using a strict version, don't forget to edit the dependency versions when upgrading.
merb_gems_version = "1.0.6.1"
dm_gems_version   = "0.9.8"
 
# For more information about each component, please read http://wiki.merbivore.com/faqs/merb_components
dependency "merb-action-args", merb_gems_version
dependency "merb-assets", merb_gems_version  
dependency "merb-cache", merb_gems_version   
dependency "merb-helpers", merb_gems_version 
dependency "merb-mailer", merb_gems_version  
dependency "merb-slices", merb_gems_version  
dependency "merb-auth-core", merb_gems_version
dependency "merb-auth-more", merb_gems_version
dependency "merb-auth-slice-password", merb_gems_version
dependency "merb-param-protection", merb_gems_version
dependency "merb-exceptions", merb_gems_version
dependency "merb_datamapper", merb_gems_version
dependency "merb-haml", merb_gems_version
 
dependency "dm-core", dm_gems_version         
dependency "dm-aggregates", dm_gems_version   
dependency "dm-migrations", dm_gems_version   
dependency "dm-timestamps", dm_gems_version   
dependency "dm-types", dm_gems_version        
dependency "dm-validations", dm_gems_version  
 
dependency "do_postgres", "0.9.9"
dependency "rack", "0.4.0"

I came to this list of dependencies by trial and error, and versions will obviously change :)

Some of the more noticeable things are including merb_datamapper, merb-haml (since I use HAML in my project, and the depdency for rack, since Passenger uses Rack. My app also uses PostgreSQL, so I included that one.

Once you have your dependencies setup, per the screencast, just do a thor merb:gem:install and check in the generated files, minus things in gems/gems/ and gems/specifications/. You’ll end up checking in a bunch of gems from gems/cache/.

Get ready to deploy!

The next step is to modify your deploy.rb file for Passenger.

config/deploy.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
set :application, "merb-dev"
 
set :hostname,    "ubuntu"
set :use_sudo,    true
set :deploy_to,   "/data/#{application}"
 
set :user,        "deploy"
set :password,    "changeme123"
 
role :app, hostname
role :web, hostname
role :db,  hostname, :primary => true
 
namespace :deploy do
  desc "setup permissions"
  task :chmod_files, :roles => :app do
    sudo "chown -R deploy:deploy /data"
  end
 
  desc "Restart Application"
  task :restart, :roles => :app do
    run "touch #{current_path}/tmp/restart.txt"
  end
 
  desc "Redeploy gems"
  task :redeploy_gems, :roles => :app do
    run "cd #{current_path} && thor merb:gem:redeploy"
  end
end
 
after "deploy:setup", "deploy:chmod_files"
before "deploy:restart", "deploy:redeploy_gems"

There isn’t much special going on here, but I’ll explain a few things.

The first is the custom task called chmod_files (lines 15-18) which runs after a cap deploy:setup task. This is a one-time task that will setup file permissions for the /deploy directory to allow the deploy user to write files. Obviously if you don’t use a deploy user, you’ll want to setup some sort of group permissions for the directory structure.

The second task is an overridden restart task (lines 20-23) which just touches the restart.txt file for Passenger, signaling a restart.

The last task, redeploy_gems uses the thor tasks to redeploy the gems listed dependencies.rb and bundled in gems/cache/. The cool thing about this task is that it will recompile any gems that use native extensions, so it doesn’t really matter what system you’re deploying to.

Once you’re setup, and all the stuff has been checked into your repo, you should be good to go:

cap deploy:setup
cap deploy

And your app should be deployed to your shiny new vhost!

Leave a Reply