Understanding The Gemfile.lock File

Published

If you’re not familiar with Gemfiles and gem versioning, this guide will make more sense if you start with my guide on updating gems.

The very first time you run bundle install in a Ruby project that has a Gemfile, Bundler will automatically create a file called Gemfile.lock. That is the authoritative source of the gems your project uses, and their versions.

Gemfile.lock makes sure that when you share your project with others, or when you deploy it, everyone will be using the exact same versions of all the gems when they run bundle install.

Here’s an example to illustrate the importance of Gemfile.lock and why you should always include it (i.e. check it into version control), even if you’re building a gem:

Annabelle starts a new Rails app on her computer, and she specifies the Rails gem in her Gemfile like this:

gem "rails", "~> 6.0"

then she runs bundle install. The latest version at that time is 6.0.3. Over the next month, Rails releases several new versions, and the latest is now 6.1.1.

During this time, Annabelle hasn’t run bundle update rails, so she’s still using version 6.0.3. Then a month later, she asks Ryan to collaborate on the app. She shares the repo with him, but doesn’t include Gemfile.lock.

Ryan runs bundle install on his computer, and he gets the latest 6.x version, which is 6.1.1, but is not compatible with some of the code Annabelle wrote that only works with version 6.0.3. If Annabelle had included Gemfile.lock in the repo, Ryan would have Rails 6.0.3 after running bundle install, and the app would be working for him.

Important! Gemfile.lock is automatically generated when you run bundle install or bundle update. It should never be edited manually.1

Luckily, Gemfile.lock is included by default, so for the most part, you shouldn’t have to worry about it, but there are a couple scenarios worth mentioning.

Bundled With

When you run bundle install, the version of Bundler that is used at the time is captured at the bottom of the Gemfile.lock. It will say something like this:

BUNDLED WITH
   2.2.8

If you try to run bundle install on a project where the Gemfile.lock was bundled with an older version of Bundler, you might get an error like this:

Traceback (most recent call last):
    2: from ~/.gem/ruby/2.7.2/bin/bundle:23:in `<main>'
    1: from ~/.rubies/ruby-2.7.2/lib/ruby/site_ruby/2.7.0/rubygems.rb:300:in `activate_bin_path'
~/.rubies/ruby-2.7.2/lib/ruby/site_ruby/2.7.0/rubygems.rb:281:in 
`find_spec_for_exe': Could not find 'bundler' (1.17.3) required by your 
~/playground/testing_gemfiles/Gemfile.lock. (Gem::GemNotFoundException)
To update to the latest version installed on your system, run 
`bundle update --bundler`.
To install the missing version, run `gem install bundler:1.17.3`

To automatically update the Gemfile.lock with your current version of Bundler, run bundle update --bundler. In general, it’s a good idea to use the latest version of Bundler. That’s why my Ruby on Mac script is meant to be run often to keep your system up to date with the latest versions of Bundler and Rubygems. If for some reason you have to keep using the older version that was originally in Gemfile.lock, you can install it using the command mentioned in the error message.

Platforms

Another aspect of Gemfile.lock that can cause confusion is the platforms section. It might look like this:

PLATFORMS
  x86_64-darwin-19

This specifies the operating systems where the bundle can be installed and deployed. Every time bundle install is run on a new operating system used for development, the platforms sections gets updated. For example, if Isabelle is on Windows and is the first to work on the project, after she runs bundle install, the Gemfile.lock will show the Windows platform:

PLATFORMS
  x64-mingw32

Then, if Adrien runs bundle install on his Intel Mac, the Mac platform will get added to the list:

PLATFORMS
  x64-mingw32
  x86_64-darwin-19

However, if you try to deploy the project to a web server using Heroku, GitHub Pages, Netlify, or other similar services, it might fail. That’s because most web servers run the Linux operating system, and if your Gemfile.lock doesn’t include the Linux platform, depending on which version of Bundler your Gemfile.lock was created with, and which version the deployment service uses, you might either get an obscure error message such as this one when deploying a Rails app to Heroku:

!     Could not detect rake tasks
!     ensure you can run `$ bundle exec rake -P` against your app
!     and using the production group of your Gemfile.
!     /tmp/build_5cb36f76/bin/rake:4:in `require': cannot load such file -- rake (LoadError)
!     from /tmp/build_5cb36f76/bin/rake:4:in `<main>'
!
remote: /tmp/codon/tmp/buildpacks/50d5eddf222a9b7326028041d4e6509f915ccf2c/lib/language_pack/helpers/rake_runner.rb:106:in `load_rake_tasks!': Could not detect rake tasks (LanguagePack::Helpers::RakeRunner::CannotLoadRakefileError)
remote: ensure you can run `$ bundle exec rake -P` against your app
remote: and using the production group of your Gemfile.
remote: /tmp/build_5cb36f76/bin/rake:4:in `require': cannot load such file -- rake (LoadError)

Or a helpful error like this:

Your bundle only supports platforms ["x86_64-darwin-19"] but your local
platform is x86_64-linux. Add the current platform to the lockfile with 
`bundle lock --add-platform x86_64-linux` and try again.

These errors are relatively new. They started happening when Bundler changed the way platforms work late last year when they released version 2.2. Before, when projects were bundled with Bundler version 2.1.x, the platforms section in Gemfile.lock would automatically include the ruby platform, along with the current operating system, like this:

PLATFORMS
  ruby
  x86_64-darwin-19

This allowed Ruby apps to be deployed on Linux servers. But since version 2.2, the ruby platform is no longer included by default. Now it’s up to each of us to remember to add the appropriate platforms. So, if we plan to deploy to a Linux server, we need to add the Linux platform with this command:

bundle lock --add-platform x86_64-linux

In some cases, this is not enough to get the deploy to work. For example, Heroku is still using the older Bundler version 2.1.4, even if your app was bundled with a newer version. It does this by removing the BUNDLED WITH section in your Gemfile.lock before running bundle install. So, if your app was bundled with Bundler version 2.2.x, to be able to deploy it to Heroku, you need to add both the Linux and the Ruby platforms. So, in addition to the command you ran above, run this one as well:

bundle lock --add-platform ruby

Heroku is aware of this and they are thinking through the various ways they might be able to address it. If you’re curious, there are interesting discussions in the rubygems repo and heroku-buildpack-ruby repo.

To summarize, if you plan to deploy your Ruby project, and if the Gemfile.lock is missing the Linux and Ruby platforms, make sure to add them.


  1. Unless there are merge conflicts, but that’s for another time.