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!