skip to content

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?

level: seniorimportance: should knowfreq 40%

answer

  1. PLATFORMS lists what the lock resolved for
  2. non-frozen install adds the local platform
  3. frozen mode cannot add it
  4. "Your bundle only supports platforms"
  5. bundle lock --add-platform x86_64-linux

basics

~20 s

The 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 s

Gems 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
bash
# 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 platform

go deeper

for a junior

Recall that gems with native code can differ per operating system and that Gemfile.lock lists the platforms it was resolved for.

for a middle

Explain why a normal install adds the local platform while a frozen install cannot, and what the error message tells you to run.

for a senior

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.

for a principal

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