Sorry this page looks weird. It was automatically migrated from my old blog, which had a different layout and different CSS.

Using rbenv in Production

I just switched to using rbenv in production so I could easily upgrade to Ruby 1.9.2-p290. 37signals uses rbenv in production like this:

Each app is isolated through disciplined use of RubyGems/Bundler-style binstubs. We start with the stubs Bundler generates (bundle install --binstubs) and change the shebang from #!/usr/bin/env ruby to usr/bin/env ruby-local-exec. Update your Capistrano recipes, Chef cookbooks, Bluepill config, etc to use these binstubs throughout.

This draws a solid line between your app and your ops, meeting at just a handful of binstubs.

Now executing /u/apps/basecamp/current/bin/unicorn uses the Ruby in /u/apps/basecamp/current/.rbenv-version and the Unicorn bundled in /u/apps/basecamp/current/Gemfile.lock. Your app is self-contained. Your ops tools no longer know or care whether you use Bundler or whether you’re on 1.8.7 or 1.9.3. Achievement unlocked.

Jeremy Kemper

Shebangs and binstubs

As far as I can tell, binstubs have two advantages:

However I couldn’t get the Rails runner to work via a binstub, and I don’t mind if my “ops tools” know about Bundler. So I decided to forgo the binstubs and stick with bundle exec. Since I’m using the same Ruby everywhere on the server, via rbenv global 1.9.2-p290, it’s fine.

But I did need to update my app’s scripts' shebangs to ruby-local-exec; for example script/rails:

#!/usr/bin/env ruby-local-exec
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.

APP_PATH = File.expand_path('../../config/application',  __FILE__)
require File.expand_path('../../config/boot',  __FILE__)
require 'rails/commands'

Passenger

Don’t forget to re-install Passenger in your rbenv-managed Ruby, and rbenv rehash afterwards.

And on Apache, you’ll also need to update the Passenger configuration to use the new Ruby.

Non-app gems

You’ll also need to install (and rbenv rehash) non-Gemfile gems, such as those your crontab commands use. For example I use a gem via cron to backup the database to S3.

Conclusion

With rbenv managing Ruby, and Bundler managing my app’s gems, everything is nicely isolated. As Jeremey Kemper went on to say:

Now, say Ruby 1.9.3-rc1 just came out and you want to upgrade your app. Push the new package to your servers (using Chef or whatever), change .rbenv-version from 1.9.3-preview1 to 1.9.3-rc1, and cap deploy. That’s it.

Didn’t work out? cap deploy:rollback and you’re back at 1.9.3-preview1. Ahh.

Which is lucky, because my Rails 3.0.10 app takes a glacial 3m47s to start up on Ruby 1.9.2-p290. As my friend Kamyar says, that’s not a computer number: ~4 minutes on modern CPUs corresponds to gazillions of instructions and we are not modelling the climate here.

So I’m going to take advantage of my easily-upgradable environment and upgrade to 1.9.3-rc1 as soon as possible.

Andrew Stewart • 20 October 2011 • RubyDeployment
You can reach me by email or on Twitter.