Deployment Best Practices with Engine Yard and Bundler 1.0
Bundler 1.0 was recently released and it is better than ever. The dependency resolution is now smarter and more flexible, a ton of bugs were fixed, and it has generally gotten much easier to use.
We have been using Bundler at Engine Yard since the first version that was actually part of Merb. Things have changed a lot since then, but the basics are actually still pretty much the same: tell the tool what your dependencies are, it will figure out what set of gems satisfy those dependencies and make them available to your application. Now that Bundler is at 1.0 and is stable and solid, I highly recommend that everyone use it to deploy their applications.
There are a few best practices that will make using Bundler to deploy work seamlessly.
Check in your Gemfile.lock
This was always a best practice when using Bundler in earlier versions. If you don’t check in the lockfile, the only thing Bundler can do when deploying, is resolve all of the gems. If a new gem was released since you last tested, you could end up deploying to production with a different set of dependencies than you tested. In Bundler 1.0, when using the –deployment flag, it is required that you check in your lockfile. It will not let you deploy without one. At Engine Yard we use –deployment whenever you are using Bundler 1.0.
Your bundle should be shared across deploys, but not across applications
When you deploy your code, it would be better if Bundler did not have to rebuild every gem in your application’s dependencies. You can do this by having Bundler install the gems in a place that is shared between deploys. At Engine Yard we automatically tell Bundler to put your gems in /data/app-name/shared/bundled_gems. This way you still get the benefit of having your application gems separate from your system gems, but you also don’t have to rebuild every gem on every deploy.
Use groups to limit what is installed on production
Most applications have a set of dependencies that they may only want during development of their application. This could be test frameworks like rspec or cucumber, or a gem used for debugging locally like ruby-debug.
When deploying with Engine Yard, Bundler does not automatically install or make available anything in the development or test groups. Your Gemfile could look something like this:
gem "rails"
group :test do
gem "rspec", :require => "spec"
end
group :development do
gem "ruby-debug"
end
Then when you deploy to production, you will get the rails gem and all of its dependencies, but ruby-debug and rspec will not be installed.
Package your gems before deployment
This recommendation is a bit more of a gray area. Bundler includes the bundle package command that will put the packed .gem files into your application so when you deploy, everything your app needs is already there. This makes your deployments less failure prone in the case that the gem servers are down.
In reality gemcutter is not down often, and hopefully if you follow recommendation #2, most deploys should not even need to fetch any gems (except when the dependencies of your application change). This also means that there are a few more megabytes of data in your git repo.
I still think it is a good idea to be resilient to those kinds of failures, but there is a trade off.
Bundler 1.0 is an amazing tool that completely changes the way you think about dependencies and deployment. I feel much more comfortable knowing that any deploy that I do using Bundler is not going to randomly fail because I forgot to install a gem on my production server.
With these few tips, deploys should be even more foolproof when it comes to your applications’ dependencies.
Share your thoughts with @engineyard on Twitter