With Bundler 4, why does `bundle install --without test --path vendor/bundle` fail, and how are those settings expressed with `bundle config`?
answer
- remembered flags are gone
- InvalidOption: use bundle config set
- --local writes .bundle/config
- local, then ENV, then global
- BUNDLE_WITHOUT, BUNDLE_PATH
basics
~20 sBundler 4 removed install flags that used to be silently remembered; --without and --path raise InvalidOption. Set them with bundle config set --local without test and bundle config set --local path vendor/bundle, or BUNDLE_WITHOUT and BUNDLE_PATH.
solid answer
~40 sIn Bundler 2, `bundle install --without test --path vendor/bundle` also wrote those values into `.bundle/config`, so every later command silently kept them. Bundler 4 dropped that behaviour, and `--without`, `--with`, `--path`, `--deployment`, `--frozen`, `--system`, `--clean`, `--shebang` and `--no-prune` now raise `InvalidOption`, naming the `bundle config set` command to use instead. The replacements are `bundle config set --local without test` and `bundle config set --local path vendor/bundle`. `--local` writes `<project>/.bundle/config` (or `$BUNDLE_APP_CONFIG/config`), `--global` writes `~/.bundle/config`, and without either flag `set` writes locally inside a project. Every key has an environment form, `BUNDLE_WITHOUT`, `BUNDLE_PATH`, and Bundler reads local config first, then the environment, then global config, then defaults. With `path` set, gems land under `vendor/bundle/ruby/4.0.0`; without it, Bundler 4 still installs into RubyGems' own directory.
code
bash · 7 lines# Bundler 4: settings instead of remembered flags
bundle config set --local without "development test"
bundle config set --local path vendor/bundle
bundle install # gems under vendor/bundle/ruby/4.0.0
bundle config get without # value and where it was set
BUNDLE_WITHOUT=test bundle install # env form, loses to .bundle/configgo deeper
Recall that Bundler 4 sets groups to skip and the install path with bundle config set, not with bundle install flags.
Explain local versus global config files, the BUNDLE_ environment forms, the lookup order, and what path and without change on disk.
Migrate Bundler 2 CI and Dockerfiles off remembered flags, prefer environment settings in images, and debug precedence with bundle config get.
Standardise where each environment's Bundler settings live, image, CI or repository, so builds are reproducible without hidden per-machine config.
## Why the old command fails Bundler 2 had a feature many people never noticed: some `bundle install` flags were **remembered**. Running `bundle install --without test --path vendor/bundle` once also wrote `without` and `path` into `.bundle/config`, so every later `bundle install`, `bundle exec` and `Bundler.setup` quietly kept skipping `test` and using `vendor/bundle`. That surprised people constantly: a flag typed once months ago still shaped every install. Bundler 4 removed remembered flags entirely. The affected `bundle install` options are marked "(removed)" and raise `InvalidOption`: > The `--without` flag has been removed because it relied on being remembered across bundler invocations, which bundler no longer does. Instead please use `bundle config set without 'test'`, and stop using this flag | Removed flag | Replacement | |---|---| | `--without test` | `bundle config set --local without test` | | `--with debug` | `bundle config set --local with debug` | | `--path vendor/bundle` | `bundle config set --local path vendor/bundle` | | `--system` | `bundle config set --local path.system true` | | `--deployment`, `--frozen` | `bundle config set --local deployment true` / `frozen true` | (`--binstubs` is gone too, replaced by the `bundle binstubs` command.) ## Where configuration lives `bundle config set` writes to one of two files: - **`--local`** - `<project_root>/.bundle/config`, or `$BUNDLE_APP_CONFIG/config` when `BUNDLE_APP_CONFIG` is set. Applies to this project. - **`--global`** - `~/.bundle/config`. Applies to every project for this user. - **Neither** - `set` writes locally when run inside a project, globally otherwise. `bundle config get NAME` shows the value and where it was set; `bundle config` alone lists everything; `bundle config unset NAME` removes it. Every key also has an **environment variable** form: upper-case it and prefix `BUNDLE_` (dots become `__`). So `without` is `BUNDLE_WITHOUT` and `path` is `BUNDLE_PATH`. Bundler resolves a key in this order: 1. Local config (`.bundle/config`) 2. Environment variables 3. Global config (`~/.bundle/config`) 4. Bundler's defaults A stale `.bundle/config` in a project therefore beats a `BUNDLE_WITHOUT` exported in CI - a classic source of "why is it still skipping that group?" Check it with `bundle config get without`. ## `without`, `with` and `path` in practice - **`without`** - a space- or colon-separated list of groups not to install, such as `development test` for a production image. The skipped groups are still resolved and locked, so production runs the versions you tested. - **`with`** - groups to install that would otherwise be skipped, mainly groups declared `optional: true` in the Gemfile. - **`path`** - where gems are installed. Bundler appends the engine and ABI version, so `vendor/bundle` becomes `vendor/bundle/ruby/4.0.0/`. Without `path`, Bundler 4 installs into RubyGems' own directory (`Gem.dir`), shared with `gem install`; the bundle-config man page says Bundler 5 will default to a `.bundle` directory in the project instead. In a container build the environment form is usually cleanest, because nothing is written into the image's project directory: ```bash export BUNDLE_WITHOUT="development test" export BUNDLE_PATH=/usr/local/bundle bundle install ``` ## Neighbouring settings A few other keys are easy to confuse with these: - **`only`** (`BUNDLE_ONLY`) - install *only* the listed groups. Gems outside any group sit in `default`, so `only test:default` is usually what you mean. - **`path.system`** (`BUNDLE_PATH__SYSTEM`) - install into RubyGems' default system directory (`Gem.dir`); the replacement for `--system`. - **`bin`** (`BUNDLE_BIN`) - the directory `bundle binstubs` writes to. - **`BUNDLE_APP_CONFIG`** - moves the local config file out of `<project>/.bundle`, handy when the project directory is read-only in a container. - **`BUNDLE_IGNORE_CONFIG`** - makes `bundle` ignore all configuration files, a quick way to test whether a stray config is to blame. ## Traps - Copying Bundler 2 CI snippets that pass `--without` or `--path`; they now stop the build. - Committing `.bundle/config` with machine-specific values such as a local `path`. - Forgetting that local config outranks the environment.
- CI exports `BUNDLE_WITHOUT=development`, yet `bundle install` also skips `test`. Where do you look?At local configuration: `bundle config get without` shows every place the key is set. A `.bundle/config` in the checkout, perhaps committed by mistake or restored from a cache, outranks environment variables, so its `without: development:test` wins. Delete or `bundle config unset --local without` it.
- What changes when you set `path` to `vendor/bundle` instead of leaving it unset?Gems install under `vendor/bundle/ruby/<abi>/` inside the project rather than into RubyGems' shared directory, so `gem list` no longer shows them and each project gets an isolated copy. It is common in CI caches and deployments; `bundle exec` or binstubs are then required, since plain RubyGems does not look there.
saying these in an interview costs you the question
- bundle install --without test still works in Bundler 4
- Environment variables always override .bundle/config
- bundle config set --global writes into the project's .bundle directory
- Groups in without are left out of Gemfile.lock
- Bundler 4 installs gems into a project .bundle directory by default