Why can a Gemfile.lock generated on an Apple-silicon Mac break a frozen Linux CI install, and what does Bundler's `bundle lock --add-platform` do about it?
answer
- PLATFORMS lists what the lock resolved for
- non-frozen install adds the local platform
- frozen mode cannot add it
- "Your bundle only supports platforms"
- bundle lock --add-platform x86_64-linux
basics
~20 sThe lockfile's PLATFORMS lists only arm64-darwin, and frozen mode is not allowed to add the CI machine's platform, so Bundler refuses. bundle lock --add-platform x86_64-linux re-resolves for that platform and records it, without needing a Linux machine.
solid answer
~40 sGems with native code often ship precompiled variants per platform, so Bundler resolves Gemfile.lock for the platforms listed under `PLATFORMS`. On a normal `bundle install`, a machine whose platform is missing simply adds it to the lockfile, and a lockfile that also lists `ruby` lets any machine fall back to source variants. Frozen mode forbids adding the platform, so when PLATFORMS holds only `arm64-darwin` - for example because some gem ships only precompiled builds - Bundler raises "Your bundle only supports platforms ["arm64-darwin"] but your local platform is x86_64-linux" and suggests the fix. `bundle lock --add-platform x86_64-linux` adds the platform and re-resolves, picking platform-specific gem variants and recording them, all from the Mac; commit the result. `--remove-platform` drops a platform, and `bundle platform` shows what the app currently supports.
code
bash · 6 lines# on the Mac: record Linux as a supported platform
bundle lock --add-platform x86_64-linux
git add Gemfile.lock
# inspect what the lockfile now supports
bundle platformgo deeper
Recall that gems with native code can differ per operating system and that Gemfile.lock lists the platforms it was resolved for.
Explain why a normal install adds the local platform while a frozen install cannot, and what the error message tells you to run.
Set up the platform list for laptops, CI and production images, read platform hunks in review, and choose between precompiled variants and compiling from the ruby platform.
Weigh the maintenance cost of supporting many platforms in one lockfile against standardising developer and CI environments.
## Platforms in Bundler A **platform** names the CPU and operating system a gem build targets. Pure-Ruby gems use the generic platform **`ruby`**. Gems with C extensions often publish extra **precompiled** variants - for example `nokogiri` builds tagged `x86_64-linux` or `arm64-darwin` - so installs skip a slow native compile. Because the right variant differs by machine, the resolution itself differs by platform, and Bundler records which platforms a lockfile was resolved for in its **`PLATFORMS`** section. Platform-specific gems also show the platform in the `specs:` entry, for instance `nokogiri (<version>-arm64-darwin)`. ## What happens on the Mac, and then in CI 1. A developer on an Apple-silicon Mac runs `bundle install` for a new project. Bundler records the local platform, and adds `ruby` as well only when every locked gem also has a `ruby`-platform release. One gem that ships only precompiled builds is enough to leave `PLATFORMS` listing `arm64-darwin` alone; lockfiles written by older Bundler releases often look the same. 2. The lockfile is committed. 3. CI runs on `x86_64-linux` with frozen mode on (`bundle config set frozen true`, or deployment mode). 4. Without frozen mode, Bundler would add `x86_64-linux` to the lockfile and resolve the Linux variants - a lockfile change. Frozen mode forbids that change, so Bundler skips adding the current platform and then validates it: ``` Your bundle only supports platforms ["arm64-darwin"] 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. ``` The error is a `Bundler::ProductionError`. The check passes if one of the locked platforms matches the machine, or if `ruby` is listed, because the source variants can then be installed and compiled on any system. ## The fix: `bundle lock --add-platform` `bundle lock` creates or updates the lockfile **without installing**. With `--add-platform`, Bundler adds the platform and re-resolves, choosing the gem variants that platform needs - all without having a Linux machine at hand: - `bundle lock --add-platform x86_64-linux` - add one platform; the option is repeatable, so you can add several. - `bundle lock --remove-platform x86_64-darwin` - drop a platform nobody uses any more. - `bundle lock --normalize-platforms` - clean up the platform list. - `bundle platform` - show the local platform and the platforms the app's gems support. Then review the diff: new `PLATFORMS` lines, new platform-tagged `specs:` entries and their `CHECKSUMS` lines. Commit it and CI's frozen install succeeds. ## Related settings and choices | Option | What it does | When it fits | |---|---|---| | add the CI and production platforms | resolves precompiled variants for each | the usual fix for Mac developers and Linux CI | | keep `ruby` in PLATFORMS | lets any platform install source variants | when compiling native gems in CI is acceptable | | `force_ruby_platform` setting | ignores the local platform, installs only `ruby` gems and compiles extensions | when a precompiled variant misbehaves on a system | Since Bundler 4.0.3, if a precompiled variant turns out incompatible with the machine, Bundler falls back to the `ruby` platform gem. ## Choosing the platform list List the platforms the application actually runs on, and no more, because every platform adds `specs:` and `CHECKSUMS` lines to review: - `arm64-darwin` for Apple-silicon laptops; - `x86_64-linux` for most CI runners and production containers; - `aarch64-linux` for ARM-based Linux servers; - `ruby` when you are willing to compile native extensions wherever no precompiled build matches. When a platform stops mattering - the last Intel Mac is retired, say - `bundle lock --remove-platform x86_64-darwin` shrinks the file again. Bundler's error message always names the exact platform string to add, so copy it from there rather than guessing. ## Preventing a repeat - Add every platform the app runs on - developer laptops, CI and production images - when the project starts. - Treat a `PLATFORMS` hunk in review as meaningful: a developer on a new operating system silently adds a line on their first non-frozen install. - Keep CI frozen: the platform error is exactly the kind of drift you want caught before deployment, not papered over by a runner rewriting its own lockfile.
- Why does a Linux developer's first `bundle install` on this project change Gemfile.lock?Outside frozen mode Bundler adds the machine's platform when no locked platform matches it, re-resolving for Linux variants. That is the same change CI is forbidden to make. Committing it, or adding the platform up front with `bundle lock --add-platform`, keeps the file stable.
- The lockfile lists `ruby` as well as `arm64-darwin`. Does the frozen Linux install fail?No. With `ruby` in PLATFORMS the platform check passes, and Bundler can install the source variants, compiling native extensions on the CI machine. The build works but is slower and needs compilers and headers; adding `x86_64-linux` lets it use precompiled variants instead.
saying these in an interview costs you the question
- PLATFORMS records which Ruby versions the app supports
- Only a Linux machine can add x86_64-linux to the lockfile
- Frozen mode adds the missing platform automatically
- The fix is to set BUNDLE_FROZEN=false in CI
- Deleting PLATFORMS makes the lockfile platform-independent