Should You Commit Gemfile.lock?
If you are coming from the JavaScript ecosystem, Gemfile.lock is Ruby's equivalent of package-lock.json. It is the lock file generated by Bundler, Ruby's dependency manager. And like its JavaScript counterpart, it triggers the same question every time a new developer sees thousands of lines of auto-generated content appear in a diff: should this be in version control?
For applications: yes, always commit Gemfile.lock. For gems (libraries): it depends, but most gem authors commit it too. The reasoning is straightforward once you understand what the file does and who consumes it.
What Gemfile.lock contains
Your Gemfile declares dependencies with version constraints:
source "https://rubygems.org"
gem "rails", "~> 7.1"
gem "pg", "~> 1.5"
gem "sidekiq", "~> 7.0"
The ~> operator (called the pessimistic version constraint) works similarly to ^ in npm. ~> 7.1 means any version from 7.1.0 up to but not including 8.0. ~> 1.5 allows 1.5.0 through 1.9.x but not 2.0.
When you run bundle install, Bundler resolves those constraints against the RubyGems registry, selects specific versions, and writes the results to Gemfile.lock. That file records three things:
- Exact versions — not ranges, but the precise version installed for every gem (e.g.,
rails (7.1.5)) - The full dependency resolution tree — every transitive dependency, including dependencies of dependencies, all pinned to exact versions
- Platforms — which platforms (Ruby version, OS) the resolution applies to (e.g.,
ruby,x86_64-linux,arm64-darwin)
A typical Rails application has 40-80 direct gems and 150-300 total packages after transitive dependencies are resolved. Gemfile.lock pins every single one.
Why you commit it for applications
Without a committed Gemfile.lock, every bundle install resolves version ranges fresh against the RubyGems registry. The consequences are the same ones that plague any ecosystem without lock files:
Version drift between developers. You install on Monday and get sidekiq 7.2.1. Your co-founder installs on Thursday and gets sidekiq 7.2.2. Patch releases are usually safe. Sometimes they are not. Now you have divergent runtime behavior with no obvious cause.
Version drift between local and production. You test locally, deploy to Heroku or Render, and the server resolves different versions because it ran bundle install at a different point in time. Your staging environment works, production does not, and the dependency mismatch is invisible unless you think to check.
No reproducible builds. Six months from now, you clone the project and run bundle install. Without a lock file, you get whatever versions are current that day. If something breaks, you have no way to know whether it is your code or a dependency change.
Committing Gemfile.lock eliminates all of this. Every developer, every CI run, and every deploy gets the exact same gem versions.
bundle install --frozen for CI
In development, bundle install reads Gemfile.lock and installs those exact versions. If the lock file is present, Bundler respects it. But bundle install will also silently update the lock file if Gemfile has changed and new resolution is needed.
For CI and deployment, you want stricter behavior. Use the --frozen flag:
bundle install --frozen
This is Bundler's equivalent of npm ci. It installs exactly what Gemfile.lock specifies and fails if the lock file is missing or out of sync with Gemfile. No silent updates, no surprise version changes.
# GitHub Actions example
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: 3.3
bundler-cache: true
- run: bundle install --frozen
- run: bundle exec rails test
Heroku and most Ruby-aware deployment platforms run bundle install --frozen (or --deployment, its predecessor) by default. If your Gemfile.lock is not committed, the deploy fails immediately.
The gem/library exception
This is where Ruby diverges slightly from the JavaScript ecosystem. Bundler's own documentation says that gems (libraries published to RubyGems) can omit Gemfile.lock from version control. The reasoning: when someone installs your gem as a dependency of their application, their application's Gemfile.lock determines the final resolved versions. Your gem's lock file is irrelevant to consumers.
This is a valid point. A gem's Gemfile.lock reflects one specific resolution of its dependencies on the author's machine. It has no effect on downstream users.
However, many gem authors still commit Gemfile.lock for a practical reason: CI consistency. If your gem's test suite runs in CI, you want reproducible test runs. Without a lock file, your CI resolves fresh versions on every run. A transitive dependency update could break your build on a day you changed nothing. Committing the lock file means your CI reproduces the same environment you tested locally.
The tradeoff is straightforward:
- Omit
Gemfile.lockif you want CI to always test against the latest compatible versions, catching incompatibilities early. - Commit
Gemfile.lockif you want CI to be stable and only update dependencies when you explicitly choose to.
Neither approach is wrong. If you are building a gem and are unsure, commit it. You can always add it to .gitignore later if you prefer the floating-version approach.
Handling merge conflicts
Lock file merge conflicts in Gemfile.lock look intimidating but are trivial to resolve. Do not try to edit the file by hand. Instead:
Step 1: Resolve Gemfile first. If both branches changed Gemfile, merge that manually. It is small and human-readable.
Step 2: Accept either side of the lock file conflict. It does not matter which:
git checkout --theirs Gemfile.lock
Step 3: Regenerate.
bundle install
Bundler reads the now-correct Gemfile, resolves all dependencies, and writes a fresh, valid Gemfile.lock.
Step 4: Stage and continue.
git add Gemfile Gemfile.lock
git merge --continue
The whole process takes under a minute. Bundler handles the hard part.
Your .gitignore
For applications, do not add Gemfile.lock to .gitignore:
# Installed gems — never commit the vendor/bundle directory
vendor/bundle/
# Do NOT add Gemfile.lock here for applications
# It must be committed for reproducible builds
If you are authoring a gem and choose the floating-version CI approach, you can add it:
# Only for gems/libraries, not applications
Gemfile.lock
But again, most gem authors today commit it. When in doubt, commit it.
The bottom line
Commit Gemfile.lock for any Ruby application. Use bundle install --frozen in CI and deployment. Resolve merge conflicts by running bundle install after merging Gemfile. For gems, committing is optional but usually the safer default.
For the broader picture on which lock files and generated files belong in your repository, see Lock Files: Commit or Ignore? and The Developer's Guide to What Belongs in Your Git Repository.