A CI job using Bundler 4 with `BUNDLE_DEPLOYMENT=true` fails at `bundle install`, saying the lockfile can't be updated because frozen mode is set; what happened, and what is the fix?
answer
- the lockfile no longer matches the Gemfile
- deployment = frozen + path vendor/bundle
- Bundler::ProductionError, not a network issue
- fix: bundle install locally, commit the lock
- --frozen/--deployment flags removed in 4
basics
~20 sFrozen mode forbids any change to Gemfile.lock, and the committed lockfile no longer matches the Gemfile, usually because someone edited the Gemfile without committing the regenerated lockfile. Run bundle install on a development machine and commit the updated Gemfile.lock.
solid answer
~50 sThe `deployment` setting means `frozen` is true plus `path` set to `vendor/bundle`, and `frozen` tells Bundler to refuse any automatic change to Gemfile.lock. When `bundle install` finds that the Gemfile no longer matches the lockfile - a gem was added, removed or moved to another source - it would need to re-resolve and rewrite the lockfile, so it raises `Bundler::ProductionError` with a message like "The dependencies in your gemfile changed, but the lockfile can't be updated because frozen mode is set", listing what was added or deleted. The fix is to regenerate the lockfile where changes are allowed: run `bundle install` locally, commit Gemfile.lock with the Gemfile and push. Turning frozen off in CI would hide the drift and test an unreviewed resolution. In Bundler 4 you set the mode with `bundle config set` or `BUNDLE_FROZEN`/`BUNDLE_DEPLOYMENT`; the old `--deployment` and `--frozen` flags raise an error.
code
bash · 9 lines# CI: frozen installs into vendor/bundle (Bundler 4)
bundle config set deployment true
bundle install
# equivalent, via the environment
BUNDLE_DEPLOYMENT=true bundle install
# removed in Bundler 4: raises Bundler::InvalidOption
bundle install --deploymentgo deeper
Recall that CI installs from the committed Gemfile.lock, so any Gemfile change must be committed together with the regenerated lockfile.
Explain frozen versus deployment, how each is set with bundle config or BUNDLE_ variables, and which Gemfile changes trigger the ProductionError.
Diagnose the failure from its message, fix it at the commit rather than in CI, and migrate CI scripts off Bundler 4's removed --deployment and --frozen flags.
Argue why reproducible installs are enforced in CI and deploys, and how the team handles lockfile merge conflicts so drift is caught before it reaches the pipeline.
## The failure A CI job that reproduces production runs Bundler in **frozen mode**. It passes on most commits and then a merge fails with: ``` The dependencies in your gemfile changed, but the lockfile can't be updated because frozen mode is set You have added to the Gemfile: * faraday (~> 2.0) Run `bundle install` elsewhere and add the updated Gemfile.lock to version control. ``` Nothing is wrong with the network or the gem server. Bundler has detected that the committed **Gemfile.lock no longer describes the Gemfile**, and frozen mode forbids it from repairing that on its own. The exception class is `Bundler::ProductionError`. ## What frozen and deployment mean Two Bundler settings control this; both are read from `bundle config` or from `BUNDLE_*` environment variables: | Setting | Environment variable | Effect | |---|---|---| | `frozen` | `BUNDLE_FROZEN` | Disallow any automatic change to Gemfile.lock; commands fail unless the lockfile can be installed exactly as written | | `deployment` | `BUNDLE_DEPLOYMENT` | Equivalent to `frozen` true **plus** `path` set to `vendor/bundle` | Bundler checks `frozen` first and falls back to `deployment`, so an explicit `BUNDLE_FROZEN=false` wins over `BUNDLE_DEPLOYMENT=true`. With either on and no Gemfile.lock at all, install stops at once: "The deployment setting requires a lockfile. Please make sure you have checked your Gemfile.lock into version control before deploying." ## Common causes 1. **Gemfile edited, lockfile not committed** - the classic case: a gem added in the Gemfile, and the regenerated Gemfile.lock left out of the commit or lost in a merge. 2. **A merge conflict resolved by hand** - DEPENDENCIES and specs no longer agree after two branches each added a gem. 3. **A source changed** - a gem moved from the gem server to a `git:` or `path:` source (the message lists it under "You have changed in the Gemfile"). 4. **A damaged CHECKSUMS section** - the message then says the lockfile "is missing a CHECKSUMS entry" for a gem, typical after a hand-merged lockfile. 5. **A missing platform** - a separate error, "Your bundle only supports platforms ... but your local platform is ...", appears when the lockfile lacks the CI machine's platform; `bundle lock --add-platform` fixes that. ## The fix - On a development machine without frozen mode, run `bundle install`. Bundler re-resolves only what changed and rewrites Gemfile.lock. - Review the lockfile diff, then commit **both** Gemfile and Gemfile.lock and push. - Re-run CI. The frozen install now matches the lockfile exactly. What **not** to do: - Do not run `bundle update` inside CI to "catch up": frozen mode blocks it, and a resolution produced on a CI runner is one nobody reviewed. - Do not unset frozen in CI to make the build green: the job would then test gem versions that no developer ran and that production may not get. - Do not turn deployment mode on for development machines; the man page warns it makes every Gemfile edit an error. ## Bundler 4 and the removed flags Older guides tell you to run `bundle install --deployment` or `bundle install --frozen`. Bundler 4 removed every CLI flag that relied on being remembered across invocations. Passing one now raises `Bundler::InvalidOption` with a message telling you to use `bundle config set deployment true` (or `frozen true`) instead. The same removal covers `--path`, `--without`, `--with`, `--system`, `--clean`, `--shebang` and `--no-prune`. In a CI script, either run `bundle config set deployment true` before `bundle install` or export `BUNDLE_DEPLOYMENT=true` in the job's environment. ## Reproducing the failure before you push You do not need CI to see the error. On your machine, `BUNDLE_FROZEN=true bundle install` runs the same check for one command without touching your saved configuration. If it passes locally, the committed lockfile matches the Gemfile. Unless `BUNDLE_FROZEN` itself is set in the environment, Bundler's message can also suggest running `bundle config set frozen false` "if this is a development machine" - advice meant for laptops that were frozen by accident, never for the CI job. ## Why the check belongs in CI Frozen mode turns lockfile drift into a **fast, explicit failure** at the start of the job instead of a silent re-resolution. The build then tests exactly the gems in the reviewed lockfile, the ones production will install from the same file.
- A job exports both `BUNDLE_DEPLOYMENT=true` and `BUNDLE_FROZEN=false`. Is the install frozen?No. Bundler reads the `frozen` setting first and only falls back to `deployment` when `frozen` is unset, so the explicit false wins. Gems still go to `vendor/bundle` if `path` comes from deployment, but lockfile changes are allowed - usually a misconfiguration worth removing.
- Why not simply run `bundle install` without frozen mode in CI and let it fix the lockfile?Because CI would then test a resolution that nobody reviewed or committed, and the runner's rewritten lockfile is thrown away with the workspace. Production, installing from the committed file, could still fail or get different versions. The failure is the signal to fix the commit.
- What does `bundle install --frozen` do in Bundler 4?It raises `Bundler::InvalidOption`: the flag was removed because it relied on being remembered across invocations. The error tells you to run `bundle config set frozen true` instead, or set `BUNDLE_FROZEN=true` in the environment.
saying these in an interview costs you the question
- Run bundle update in the CI job so the lockfile catches up
- Pass --deployment to bundle install on Bundler 4
- Frozen mode means installed gems are made read-only
- Setting deployment on developer laptops is harmless
- Delete Gemfile.lock in CI so Bundler can resolve fresh