skip to content

Why did many Ruby teams move from RVM to rbenv, and what exactly changes for a project when they do?

level: middleimportance: should knowfreq 30%

answer

  1. RVM does everything, in your shell
  2. overrides cd, sources project scripts
  3. gemsets duplicate Bundler
  4. autolibs may sudo-install packages
  5. last release 1.29.12, January 2021

basics

~20 s

RVM hooks cd, sources shell code into every session, manages gemsets and system packages, and last tagged 1.29.12 in 2021; rbenv just selects a Ruby and leaves gems to Bundler. Migrating keeps .ruby-version and drops .rvmrc, .ruby-gemset and RVM.

solid answer

~50 s

RVM does a lot inside your shell: it redefines `cd` (or adds a zsh `chpwd` hook), sources project `.rvmrc` scripts behind a trust prompt, rewrites `PATH` and the gem variables through a shell function, keeps gemsets, and by default may install system packages with `sudo` through autolibs. That breadth caused surprises: switching fails in cron and scripts, it collides with other tools hooking `cd`, and gemsets duplicate what Bundler's lockfile already does. rbenv just selects a Ruby (a project states it in `.ruby-version`), installs through its ruby-build plugin, and leaves gem isolation to Bundler. RVM's last tagged release is 1.29.12 (January 2021), so Ruby 4 support means running master. A migration keeps `.ruby-version` (both read it), converts any `.rvmrc` with `rvm rvmrc to ruby-version`, deletes `.ruby-gemset`, fixes crontabs and service files that call RVM wrappers, and ends with `rvm implode` plus removing RVM's lines from shell profiles.

code

bash · 7 lines
bash
cd /srv/billing
rvm rvmrc to ruby-version     # only if a .rvmrc exists; deletes it
rm -f .ruby-gemset
cat .ruby-version             # ruby-4.0.7 - rbenv reads this too
crontab -l | grep rvm         # callers to rewrite first
rvm implode                   # asks for confirmation, removes rvm_path
# then delete RVM's lines from ~/.bash_profile, ~/.bashrc, ~/.zshrc

go deeper

for a junior

Recall the headline difference: RVM hooks cd and manages gemsets, rbenv only selects a Ruby and leaves gems to Bundler.

for a middle

Explain the concrete costs of RVM's design and the migration steps: .ruby-version stays, .rvmrc and .ruby-gemset go, then rvm implode.

for a senior

Plan the cut-over on servers: inventory crontabs, units and deploy scripts that call RVM wrappers before removing anything.

for a principal

Frame the choice as tooling risk: an unreleased-since-2021 manager in the deploy path versus the cost of migrating every caller.

## Two design philosophies RVM (Ruby Version Manager) and rbenv solve the same core problem, several Rubies on one machine, from opposite ends. **RVM** is a large bash program sourced into your shell that installs Rubies, selects them, manages per-Ruby gem directories and installs operating-system build dependencies. **rbenv** does one job, selecting the Ruby for a command, and hands installing to its ruby-build plugin and gem isolation to Bundler. How rbenv resolves a version is its own topic; here the point is what RVM does that teams chose to stop depending on. | Concern | RVM | rbenv | |---|---|---| | Switching | shell function rewriting `PATH` and gem variables | per-command resolution, no `cd` override | | Directory change | replaces `cd`, `pushd`, `popd` in bash; `chpwd` hook in zsh | no directory hook | | Project files | `.rvmrc` (sourced), `.versions.conf`, `.ruby-version`, `.ruby-gemset`, Gemfile | `.ruby-version` | | Gem isolation | gemsets plus `@global` | none; Bundler and the lockfile | | System packages | `rvm requirements` via autolibs, default `enabled` | not its job | | Releases | last tag 1.29.12, January 2021 | still tagged (1.3.2 at this pin) | ## The pain points behind the move - **The `cd` override.** Every directory change runs RVM's project-file search. Other tools that also wrap `cd` or `chpwd` can conflict with it, and the behaviour disappears silently in non-interactive shells, so code that works in a terminal fails in cron, systemd and CI. - **Sourced shell code.** RVM's selection only works when `rvm` is a sourced function; otherwise it prints `RVM is not a function`. `.rvmrc` files run arbitrary commands on `cd`, which is why RVM added a trust prompt and now warns that `.rvmrc` is slower and incompatible with other managers. - **Gemsets duplicate Bundler.** Once every project had a Gemfile and a lockfile and ran commands through `bundle exec`, the lockfile decided which gem versions load, and a gemset only kept a separate directory. Gemsets also leak through `@global`. - **Reach into the system.** With autolibs at its default, `rvm install` may install packages through the system package manager and call `sudo`, which surprises users on managed machines; `rvm autolibs read-fail` is the opt-out. - **Release cadence.** The last tagged release, 1.29.12, dates from 15 January 2021. Newer work, including recognising `4` as an MRI version, sits on master under an unreleased "Next" changelog section, so installing Ruby 4 means `rvm get master`. RVM still works, and some teams keep it on long-lived servers; interviewers usually want the trade-off stated fairly rather than a verdict. The honest summary is that RVM trades transparency for convenience, and that trade gets worse the more of your Ruby runs outside an interactive terminal. ## What a migration changes for a project 1. **Keep `.ruby-version`.** Both tools read it, so it becomes the single source of the Ruby version. If the project had a `.rvmrc`, run `rvm rvmrc to ruby-version` first; it writes `.ruby-version` and deletes the `.rvmrc`, which RVM would otherwise check before `.ruby-version`. 2. **Delete `.ruby-gemset`.** rbenv ignores it; per-project isolation now comes from the Gemfile and lockfile. 3. **Reinstall gems per Ruby.** Gems installed into RVM's gemsets live under `~/.rvm/gems` and are not reused; run `bundle install` under the new Ruby. 4. **Fix non-interactive callers.** Crontabs, systemd units and deploy scripts often reference `~/.rvm/wrappers/ruby-X@gemset/...`, `rvm in DIR do` or variables written by `rvm cron setup`. Each needs a new invocation before RVM is removed. 5. **Remove RVM.** `rvm implode` deletes the whole `rvm_path` tree (after a confirmation), and you remove the RVM lines from `.bash_profile`, `.bashrc`, `.zshrc` or `.profile` yourself, or new shells keep trying to source it. ## What stays the same - the `.ruby-version` file and its format, as long as it holds only a version such as `ruby-4.0.7` or `4.0.7`; - the Gemfile, the lockfile and every `bundle exec` command, because Bundler never depended on RVM; - the Ruby itself: both tools install the same CRuby releases, just into different directories. ## Answering in an interview State the model difference (a shell-resident manager versus a single-purpose one), name two or three concrete costs (`cd` hooking, gemsets versus Bundler, non-interactive failures), mention the release state, and describe the migration steps. Avoid claiming RVM cannot read `.ruby-version` or always compiles from source; both are false.

  • Is there a way to keep RVM but stop it hooking `cd`?
    Yes: set `rvm_project_rvmrc=0` in `~/.rvmrc` or `/etc/rvmrc`. RVM then no longer loads project files on directory change, and you select Rubies explicitly with `rvm use`. It removes the surprise, but also the automatic per-project switch.
  • What does `rvm implode` remove, and what does it leave behind?
    After a confirmation prompt it recursively removes the RVM installation under `rvm_path`, including installed Rubies, gemsets, wrappers and RVM's own binaries. It does not edit your shell profiles, so the lines sourcing `~/.rvm/scripts/rvm` must be removed by hand, and crontabs or unit files pointing at `~/.rvm/wrappers` must be rewritten first.

RVM is a building concierge who greets you at every door you walk through and rearranges your room each time; rbenv is a labelled key rack you check only when you need a key. The concierge is helpful until you enter through a side door he never watches, which is what cron and scripts do.

saying these in an interview costs you the question

  • Claims RVM cannot read .ruby-version files
  • Says RVM always compiles every Ruby from source
  • Thinks rbenv has gemsets that replace RVM's
  • Deletes RVM before rewriting crontabs that call its wrappers
  • Believes rvm implode also cleans the shell profile lines