skip to content

In RubyGems, what is the difference between GEM_HOME and GEM_PATH, and where does gem install --user-install put gems?

level: middleimportance: must knowfreq 45%

answer

  1. one directory to write, many to read
  2. Gem.dir vs Gem.path
  3. GEM_HOME is appended to GEM_PATH
  4. ~/.gem/ruby/4.0.0
  5. gem env home, gem env path

basics

~10 s

GEM_HOME is the single directory gem install writes into; GEM_PATH is the list of directories RubyGems searches for installed gems, and GEM_HOME is always part of it. --user-install writes to Gem.user_dir, such as ~/.gem/ruby/4.0.0.

solid answer

~40 s

`GEM_HOME` (`Gem.dir`) is where `gem install` puts new gems; without it RubyGems uses `Gem.default_dir`, Ruby's own `.../lib/ruby/gems/4.0.0`. `GEM_PATH` (`Gem.path`) lists every directory searched when a gem is activated, and RubyGems appends `GEM_HOME` to it; when `GEM_PATH` is unset the search path is the user gem directory, the default directory and `GEM_HOME`. `--user-install` writes to `Gem.user_dir`, typically `~/.gem/ruby/4.0.0`, with executables in its `bin`, which RubyGems warns about if it is not on `PATH`. When `GEM_HOME` is unset and the default directory is not writable, RubyGems switches to the user directory by itself. `gem env home`, `gem env path` and `gem env user_gemhome` print each.

code

bash · 7 lines
bash
gem env home          # /usr/local/lib/ruby/gems/4.0.0  (Gem.dir)
gem env path          # /home/dev/.gem/ruby/4.0.0:/usr/local/lib/ruby/gems/4.0.0
gem env user_gemhome  # /home/dev/.gem/ruby/4.0.0

# a project-local install directory
GEM_HOME=$PWD/.gems gem install rack -v 3.2.7
GEM_HOME=$PWD/.gems ruby -e 'puts Gem.path'   # .gems is on the search path

go deeper

for a junior

Remember the split: GEM_HOME is where installs go, GEM_PATH is where Ruby looks. Know that gem env prints both.

for a middle

Explain the defaults, that GEM_HOME is appended to GEM_PATH, where --user-install writes and why RubyGems falls back to it when the default directory is read-only.

for a senior

Diagnose a gem that installed but will not load: compare gem env output of the installing and running Ruby, check the user bin directory on PATH, and avoid root-owned installs.

for a principal

Weigh shared gem directories on build machines against per-project isolation, and decide which layer, version manager or Bundler, should own the paths.

## Two variables, two jobs RubyGems separates **where it writes** from **where it reads**. - **`GEM_HOME`** is one directory: the **installation directory**. `gem install` and `gem update` put new gems there. Inside Ruby it is `Gem.dir`. - **`GEM_PATH`** is a list of directories, separated like `PATH` (`:` on Unix): the **search path**. When Ruby activates a gem, RubyGems looks through all of them. Inside Ruby it is `Gem.path`. Each gem directory has the same layout: `gems/` (unpacked code, one folder per `name-version`), `specifications/` (one `.gemspec` per installed version), `cache/` (the downloaded `.gem` files) and `bin/` (wrapper executables, except for the default directory, which uses Ruby's own bin directory). ## The defaults when nothing is set With neither variable set: 1. `GEM_HOME` falls back to `Gem.default_dir`, which is `<rubylibprefix>/gems/<ruby_version>`, for example `/usr/local/lib/ruby/gems/4.0.0`. 2. `GEM_PATH` falls back to the **default path**: the user gem directory (when your home directory exists), then `Gem.default_dir`, then `GEM_HOME`. ## What setting them does - Setting `GEM_HOME` redirects installs and is automatically **added to the search path**, so gems you install there are always found. - Setting `GEM_PATH` **replaces** the default search list with your directories, and RubyGems appends `GEM_HOME` at the end. A trailing separator (`GEM_PATH=/opt/shared-gems:`) keeps the default directories as well. - Gems that ship inside Ruby (default gems) are found through a separate `specifications/default` directory, so they stay visible whatever `GEM_PATH` says. When the same version of a gem exists in two directories, the search order breaks the tie; when versions differ, activation still prefers the **highest matching version**, wherever it lives. ## --user-install and the automatic fallback `gem install --user-install NAME` installs into `Gem.user_dir` instead of `GEM_HOME`: - The path is `~/.gem/<engine>/<ruby_version>`, for example `~/.gem/ruby/4.0.0`. If `~/.gem` does not exist, RubyGems uses `$XDG_DATA_HOME/gem/ruby/4.0.0` (default `~/.local/share/gem/...`). - Executables go to that directory's `bin/`. If it is not on `PATH`, RubyGems warns `You don't have ... in your PATH, gem executables (...) will not run.` - The user directory is already on the default search path, so these gems load without further setup. You often get this behaviour **without asking**: when `GEM_HOME` is unset and the default directory is not writable (a system Ruby owned by root), RubyGems prints `Defaulting to user installation because default installation directory (...) is not writable.` and installs into your home instead. `--no-user-install` switches that off. ## Seeing the real values | Command | Prints | |---|---| | `gem env` | everything, including `INSTALLATION DIRECTORY`, `USER INSTALLATION DIRECTORY` and `GEM PATHS` | | `gem env home` | `Gem.dir`, the value of `GEM_HOME` | | `gem env path` | `Gem.path`, the full search list | | `gem env user_gemhome` | `Gem.user_dir`, the `--user-install` target | The error for a missing gem points the same way: `Gem::MissingSpecError` reads `Could not find 'NAME' (...) among N total gem(s)` followed by `Checked in 'GEM_PATH=...', execute `gem env` for more information`. ## Worked example: a project-local gem directory Pointing `GEM_HOME` at a directory inside a project gives it a private set of gems without touching the system install: 1. `export GEM_HOME=$PWD/.gems` makes `.gems` the install target. 2. `gem install rack -v 3.2.7` now writes into `.gems/gems/rack-3.2.7` and `.gems/specifications`. 3. Because `GEM_HOME` is appended to the search path, Ruby processes started from that shell find rack there, plus the user and default directories when `GEM_PATH` is unset. 4. Executables land in `.gems/bin`, which you add to `PATH` yourself. Setting `GEM_PATH=$PWD/.gems` as well narrows the search to that one directory (plus default gems), which is the strict isolation some build scripts want. ## Why interviewers ask - "I installed it but Ruby can't find it" is usually an install into one directory while the process searches another: a different Ruby, a different `GEM_HOME`, or a user install whose `bin` is not on `PATH`. - Version managers and Bundler work by setting these variables or their own paths, so understanding the two roles explains how they isolate projects. - `sudo gem install` into the system Ruby is the classic mistake the automatic user install exists to prevent.

  • You set GEM_PATH=/opt/shared-gems and GEM_HOME=/home/dev/gems. Which directories are searched?
    `/opt/shared-gems` and then `/home/dev/gems`, because RubyGems appends `GEM_HOME` to whatever `GEM_PATH` lists. The default install directory drops out of the list unless `GEM_PATH` ends with a separator. Default gems shipped inside Ruby remain visible either way, because their specifications are read from a separate `specifications/default` directory.
  • Why does gem install sometimes print "Defaulting to user installation"?
    Because `GEM_HOME` is unset and the default installation directory exists but is not writable by you, typically a system Ruby owned by root. Instead of failing, RubyGems installs into `Gem.user_dir` (for example `~/.gem/ruby/4.0.0`). Passing `--no-user-install` disables the fallback, and setting `GEM_HOME` avoids it.

saying these in an interview costs you the question

  • Thinks GEM_PATH is where gem install writes new gems
  • Believes setting GEM_PATH also changes the install directory
  • Fixes permission errors with sudo gem install into a system Ruby
  • Assumes --user-install executables are on PATH automatically
  • Thinks GEM_HOME must be listed in GEM_PATH by hand