Guides

Getting Started

Guides

Dependency Management

Gem Development

Publishing & Security

Integrations

Hosting & Sources

Extending

Troubleshooting

Concepts

Reference

Appendix

How Gemfile.lock works

How to read the file that pins every gem version your application installs.

Gemfile.lock is the output of dependency resolution. The first bundle install resolves your Gemfile and writes the exact version of every gem, direct or transitive, into the lockfile. Every later install reuses those versions instead of resolving again, so every machine, every teammate, and every deploy runs the same code. Bundler maintains the file. You never edit it by hand.

A lockfile, section by section

The examples below come from the lockfile that bundle install with Bundler 4.0 generates for this Gemfile:

source "https://rubygems.org"

ruby "3.4.10"

gem "rspec"
gem "rack-test", git: "https://github.com/rack/rack-test"
gem "billing", path: "vendor/billing"

GEM

Each source the Gemfile uses gets its own block, and GEM is the block for a gem server. It names the server under remote: and lists under specs: every gem resolved from it, at the exact version chosen:

GEM
  remote: https://rubygems.org/
  specs:
    diff-lcs (1.6.2)
    rack (3.2.6)
    rspec (3.13.2)
      rspec-core (~> 3.13.0)
      rspec-expectations (~> 3.13.0)
      rspec-mocks (~> 3.13.0)
    rspec-core (3.13.6)
      rspec-support (~> 3.13.0)
    rspec-expectations (3.13.5)
      diff-lcs (>= 1.2.0, < 2.0)
      rspec-support (~> 3.13.0)
    rspec-mocks (3.13.8)
      diff-lcs (>= 1.2.0, < 2.0)
      rspec-support (~> 3.13.0)
    rspec-support (3.13.7)

The lines indented under a gem are its own dependencies with the constraints from its gemspec. They explain why versions were chosen, but they are requirements, not choices. The choice for rspec-core is its top-level entry, rspec-core (3.13.6). Note that the Gemfile above asked for one gem from this server and six appear here. Transitive dependencies are locked just as precisely as direct ones.

GIT and PATH

Gems taken from a git repository or a local directory get their own source blocks, which appear before GEM in the file:

GIT
  remote: https://github.com/rack/rack-test
  revision: 1fc57f3d26275c51ba6ecea860182b94c9c242fa
  specs:
    rack-test (2.2.0)
      rack (>= 1.3)

PATH
  remote: vendor/billing
  specs:
    billing (0.1.0)

For a git source the pinned commit under revision: plays the role that the version number plays in GEM. Later installs fetch exactly that commit, even if the branch has moved on. A path source records only the location. Its contents are read from that directory on every install, which is what makes path: useful while developing a gem alongside the application.

PLATFORMS

PLATFORMS
  arm64-darwin-27
  ruby

This lists the platforms the resolution covers. ruby is the generic platform of pure-Ruby gems, and the others are concrete platforms the lockfile was resolved for, which matters for gems that ship precompiled platform-specific versions. See Platforms and native gems for the platform concept itself. If you develop on macOS and deploy to Linux, add the deploy platform so resolution covers it too:

bundle lock --add-platform x86_64-linux

Rather than curating the list by hand, normalize it before committing, as covered in When to commit it.

DEPENDENCIES

DEPENDENCIES
  billing!
  rack-test!
  rspec

These are the direct dependencies, one line per gem call in the Gemfile. Anything in the source blocks that is missing here is a transitive dependency. A trailing ! marks a gem pinned to a non-default source, one of the GIT or PATH blocks above.

CHECKSUMS

CHECKSUMS
  billing (0.1.0)
  bundler (4.0.16) sha256=d6ca5dd440c24f9abce9844cf44cc8e18c6a553de65a47efb4544137af92c47d
  diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
  rack (3.2.6) sha256=5ed78e1f73b2e25679bec7d45ee2d4483cc4146eb1be0264fc4d94cb5ef212c2
  rack-test (2.2.0)
  rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
  ...

Each checksum is the SHA-256 digest of the packaged .gem file, and Bundler verifies every gem against it during installation. A gem that was tampered with after the lockfile was written fails to install, which protects deploys against a compromised gem source. Gems from git and path sources have no packaged file to digest, so their entries carry no checksum. Bundler writes this section into new lockfiles by default. To add it to an existing lockfile, see Lockfile checksums.

RUBY VERSION

RUBY VERSION
  ruby 3.4.10

This section appears only when the Gemfile declares a ruby version, and records the Ruby the project was locked with. Without the declaration the lockfile has no opinion about the Ruby version.

BUNDLED WITH

BUNDLED WITH
  4.0.16

The Bundler version that wrote the lockfile. When another machine runs bundle commands in this project, Bundler automatically switches to this version if it is installed, so the whole team locks with the same Bundler. See Which Bundler version runs.

When to commit it

For an application, always commit Gemfile.lock. The lockfile is how a deploy or a teammate reproduces your exact gem versions, and an uncommitted lockfile silently turns every install back into a fresh resolution. For a gem, the lockfile is not part of the package. When an application depends on your gem, Bundler resolves your gemspec’s dependencies together with everything else and ignores any lockfile your gem ships, as covered in Gemfile and gemspec. Whether to commit the lockfile of the gem’s own repository for development is a separate tradeoff, discussed in the FAQs.

Before committing a lockfile, normalize its platform list. Treat this as a required step:

bundle lock --normalize-platforms

Normalizing fixes two problems at once. First, on macOS the concrete platform records the Darwin major version, arm64-darwin-27 in the example above. That number differs between macOS releases, so lockfiles generated on different Macs disagree about the platform for no useful reason. Normalizing rewrites the entry to the versionless arm64-darwin, which covers every macOS release, and later installs keep that form. Second, for gems that ship precompiled platform-specific versions, normalizing adds every platform the locked versions are precompiled for in one step, instead of one bundle lock --add-platform per deploy target. A lockfile freshly generated by Bundler 4 already locks all of those precompiled platforms, but the first problem remains. The example at the top of this page is a fresh Bundler 4 lockfile and still records arm64-darwin-27. So run the command on new lockfiles too, and on older lockfiles it catches up both points at once.

Because Bundler regenerates the file, never resolve a merge conflict in Gemfile.lock by hand. Bundler refuses to load a lockfile containing conflict markers and asks for a clean copy. Restore one side with git checkout HEAD -- Gemfile.lock, merge the Gemfile normally, then run bundle install and Bundler re-locks whatever the merged Gemfile changed while keeping unrelated pins in place.