Managing Gems

Ruby gems can be installed on Heroku via Gem Bundler or the Heroku gem manifest. Both are supported, but it it is recommended you only use one in your app in order to avoid confusion. Bundler is the up-and-coming community standard, and can be used to manage gems locally.

Gem Bundler

Gem Bundler is the default gem manager for Rails 3, and can be used with any Ruby project as it has no dependency on framework. To use, install bundler:

$ sudo gem install bundler

Create a Gemfile in the root of your app, with contents like this:


gem 'sinatra', '0.9.4'
Do not use disable_system_gems in your Gemfile

Bundle your gems by running:

$ gem bundle

The gems will be placed into vendor/gems. Make sure to add the following to your .gitignore

vendor/gems

When you push to Heroku, the Gemfile will be detected and your gems bundled automatically:

-----> Heroku receiving push

-----> Gemfile detected, running gem bundle
       Calculating dependencies...
       Updating source: http://gems.rubyforge.org
       Downloading rack-1.1.0.gem
       Downloading sinatra-0.9.4.gem
       Installing rack (1.1.0)
       Installing sinatra (0.9.4)
       Done.

Using Bundler from Sinatra or any Rack app

To use gems from your bundler environment under Sinatra or other non-Rails framework, add the following to the top of your app:


require File.dirname(__FILE__) + '/vendor/gems/environment'
Bundler.require_env

Using Bundler from Rails 2.x

If you’re using Rails below version 3.0, you’ll need to add the following line to the top of your Gemfile:


bundle_path 'vendor/bundler_gems'

Then add the following to your .gitignore

vendor/bundler_gems

Finally, create config/preinitializer.rb with the following contents:


require "#{File.dirname(__FILE__)}/../vendor/bundler_gems/environment"

class Rails::Boot
  def run
    load_initializer
    extend_environment
    Rails::Initializer.run(:set_load_path)
  end

  def extend_environment
    Rails::Initializer.class_eval do
      old_load = instance_method(:load_environment)
      define_method(:load_environment) do
        Bundler.require_env RAILS_ENV
        old_load.bind(self).call
      end
    end
  end
end

Binary gems

Gem bundler will build binary gems for you. Because your gems directory is ignored, developers who get a fresh checkout of the app will need to run “gem bundle” to build the gems for their local system.

Bundle everything

The recommended use of gem bundler is to bundle absolutely every gem your app depends upon. This includes your framework (Rails, Sinatra, etc). This doesn’t add anything to your repository size if you've gitignored the vendored gems directory.

Bundling Rails and other dependencies does increase your slug size by a few megabytes. Increased slug size is a worthwhile tradeoff for vastly simplified dependency management, as well as the flexibility of being able to use any version of Rails you wish.

Further reading

Heroku gem manifest

Another method for specifying your gem dependencies is the Heroku gems manifest, which is a .gems file in the root of your app. When a new version of the app is pushed to Heroku, any changes to the .gems file are detected and new gems are installed along with their dependencies.

System gems

All apps deployed to Heroku have access to a number of gems installed as part of the base platform. See installed-gems.heroku.com for a comprehensive listing.

Gems installed as part of your application override Heroku system gems.

Creating a gem manifest file

The gems manifest is a simple text file that includes information about each of the gems required by an application. Each entry in the manifest includes a mandatory gem name along with optional version and source options. The following is an example .gems manifest file that includes two gems:

hpricot --version '>= 0.2' --source code.whytheluckystiff.net
dm-core --version 0.9.10

Each line of the .gems file includes the following:

  • The name of the gem, as would be specified on the gem install command line.

  • An optional gem version specifier. This can be a basic version number like "0.1.2" or an advanced version specifier with comparison constraints like ">= 0.1.2" (see Specifying Versions in the RubyGems manual for more information). When no explicit version is specified, the most recent version of the gem is installed.

  • An optional gem repository source URL, like gems.rubyforge.org, gems.github.com, or gems.mycustomrepo.com. The gems.rubyforge.org repository is always included as the last source. Multiple --source arguments may be provided for cases where dependencies cross multiple repositories.

TIP: The --version and --source options have short forms just like the gem command; use -v and -s, respectively.

Deploying gem changes

When a .gems manifest file is added or modified, it must be committed to the app’s git repository and pushed to Heroku for changes to take effect. The actual gem install process occurs during the git push operation.

The following example adds a .gems file to the app’s git repository, commits it, and pushes to the remote heroku repository:

$ git add .gems
$ git commit -m 'added gems manifest file'
$ git push heroku
Counting objects: 4, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 356 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)

-----> Heroku receiving push

-----> Installing gem hpricot >= 0.2 from http://code.whytheluckystiff.net
       Building native extensions.  This could take a while...
       Successfully installed hpricot-0.6
       1 gem installed

-----> Installing gem dm-core 0.9.10 from http://gems.rubyforge.org
       Successfully installed addressable-2.0.2
       Successfully installed extlib-0.9.10
       Successfully installed data_objects-0.9.11
       Successfully installed dm-core-0.9.10
       4 gems installed

-----> Rails app detected
       Compiled slug size is 4.3MB
-----> Launching.............. done
       App deployed to Heroku

To git@heroku.com:vivid-moon-60.git
   91425e3..fe10e87  master -> master

The push is aborted if the gem manifest is invalid or a gem fails to install, ensuring that an application is not deployed in an inconsistent state.