Beginner's Guide to RVM
Working across multiple Ruby projects often means juggling different Ruby versions and conflicting gem dependencies. A client project might be stuck on Ruby 2.7 for compatibility reasons, your main application runs Ruby 3.2, and you're experimenting with Ruby 3.4 features in a side project. Without proper tooling, this becomes a maintenance nightmare.
RVM (Ruby Version Manager) tackles these challenges head-on with a comprehensive environment management system. It doesn't just switch Ruby versions. It provides gemsets for dependency isolation, handles compilation dependencies automatically, and integrates project-specific configurations seamlessly into your workflow.
This tutorial covers RVM installation, its core features, and practical workflows for managing Ruby environments effectively.
Prerequisites
You'll need a Unix-based system (Linux or macOS) to use RVM. Windows developers can access RVM through Windows Subsystem for Linux. Basic command-line familiarity is helpful, though we'll explain each step clearly.
RVM handles Ruby installation completely, so you don't need Ruby already on your system. The tool downloads, compiles, and configures everything automatically. See the official Ruby installation guide for platform details.
What makes RVM different?
RVM distinguishes itself through comprehensive environment management rather than minimal version switching. Where other tools focus narrowly on changing Ruby versions, RVM provides a complete ecosystem.
The tool integrates as a shell function, not an external program. When you type RVM commands, you're calling functions loaded into your shell that can modify your environment directly. This deep integration enables features like automatic context switching and gemset management that require manipulating environment variables dynamically.
Gemsets represent RVM's standout feature. A gemset creates an isolated gem collection within a Ruby version, letting you maintain completely separate dependencies for different projects even when they share the same Ruby version. This goes beyond what dependency management tools like Bundler provide, offering an additional isolation layer.
RVM also simplifies system dependency management. Installing Ruby from source typically requires dozens of development libraries. RVM knows which packages your system needs and can install them automatically, eliminating the frustration of tracking down missing dependencies during compilation.
The trade-off for these features is complexity. RVM modifies your shell environment more extensively than minimal alternatives and includes many commands and options. However, this complexity brings power. You get comprehensive tools for managing even intricate multi-project setups.
Setting up RVM
Installing RVM differs from typical package installations. You'll run a script that downloads RVM and configures your shell environment.
Ubuntu users need to import RVM's GPG keys first to verify the installation script:
These keys verify that you're downloading the authentic RVM installation script. The keys belong to RVM's maintainers. macOS users can skip this step as GPG isn't typically required on macOS.
Now install RVM:
The leading backslash prevents any curl alias from interfering. This command fetches the RVM installer and sets up the stable version. Unlike some installation approaches, this doesn't automatically compile Ruby. You'll install Ruby versions separately after RVM is ready.
Once installation finishes, activate RVM in your current shell:
The installer added initialization code to your shell startup files, so future terminal sessions load RVM automatically. This command just activates it now without restarting your terminal.
Confirm RVM is working:
At this point, RVM is installed but you haven't compiled any Ruby versions yet. You'll do that in the next section, which gives you control over which versions to install and whether to use pre-compiled binaries or compile from source.
How RVM manages environments
Understanding RVM's internal workings helps you troubleshoot issues and use advanced features effectively.
All Ruby installations live under ~/.rvm/rubies/, each in its own subdirectory. Unlike tools that use wrapper scripts (shims) to intercept commands, RVM modifies your environment variables to point directly at the active Ruby installation. Your PATH changes to include the current Ruby's bin directory, GEM_HOME points to the active gemset, and other variables configure the environment completely.
This environment manipulation happens through shell functions. When you run rvm use 3.2.0, you're calling a shell function that reconfigures your environment variables, not executing a separate program. This is why RVM must be loaded as a shell function rather than just having an executable in your PATH.
Gemsets add another organizational layer. Each Ruby version has its own gemsets directory at ~/.rvm/gems/ruby-X.X.X@gemset-name/. The @ symbol separates Ruby versions from gemset names in RVM commands. Every Ruby version starts with @default and @global gemsets. Default activates when you don't specify one, while global contains gems available across all gemsets for that Ruby version.
RVM can detect project configuration files and adjust your environment automatically. When you cd into a directory, RVM's shell integration checks for .ruby-version, .ruby-gemset, or .rvmrc files and switches contexts accordingly. This automation happens because RVM wraps the cd command with additional logic.
Adding Ruby versions
RVM makes installing multiple Ruby versions straightforward. You specify what you want, and RVM handles compilation and configuration.
See available Ruby versions:
This shows Ruby interpreters RVM can install: MRI (standard Ruby), JRuby, Rubinius, mruby, and others. The list includes current releases, older versions for legacy support, and preview versions.
Install a specific version:
RVM first looks for pre-compiled binaries to save time. When none exist, it compiles from source. The output shows each compilation phase, including downloading source code, configuring the build, compiling, and setting up the default gemset.
Install additional versions as needed:
View installed versions:
Symbols indicate status: => marks the current version, * shows the default, and =* means both current and default.
Switch between installed versions:
Verify the switch worked:
Set your default Ruby version for new shells:
New terminal sessions will now start with Ruby 3.4.6 active. The --default flag persists this choice across sessions.
Understanding gemsets
Gemsets provide dependency isolation within a single Ruby version. This concept is unique to RVM and offers organizational benefits beyond standard dependency management.
Think of gemsets as separate gem libraries for one Ruby version. You might create a rails6 gemset and a rails7 gemset under Ruby 3.2, keeping different Rails versions completely isolated even though they run on the same Ruby interpreter.
Create a gemset:
RVM created a new gem directory and generated wrapper scripts for gem executables.
Activate this gemset:
The @ separator combines Ruby version and gemset name. You're now in an isolated environment. Any gems you install only exist in this gemset.
Install gems:
List gemsets for your current Ruby:
The arrow shows which gemset is active. Switch to the default gemset:
Sinatra isn't here. It only exists in the api_project gemset. This demonstrates complete isolation.
The global gemset serves a special purpose. Gems installed there become available across all other gemsets for that Ruby version:
Now bundler is accessible from any gemset under Ruby 3.4.6 without installing it repeatedly.
Remove gemsets you no longer need:
This deletes the gemset and all its gems permanently.
Modern Ruby development typically uses Bundler for dependency management, making gemsets less critical than they once were. Many developers now use gemsets sparingly or not at all, relying on Bundler's Gemfile approach instead. However, gemsets remain useful for organizing tool installations or maintaining completely separate environments for testing different configurations.
Automatic project environments
RVM can detect project requirements automatically and switch environments when you navigate directories. This eliminates manual version management during development.
Create a project directory:
Specify the required Ruby version:
Leave the directory and return:
RVM detected the .ruby-version file but can't switch because Ruby 3.4.4 isn't installed yet. Install it:
Specify a gemset as well:
RVM created the blog gemset automatically and activated it. Now whenever you work in this directory, you'll have the correct Ruby version and gemset active.
For version control, commit .ruby-version (and optionally .ruby-gemset) to your repository. Team members with RVM will automatically use the correct environment when they clone and work on the project.
An alternative approach uses .rvmrc files:
The .rvmrc file can contain more complex logic since RVM executes it as shell code. However, this creates security concerns. Arbitrary code in .rvmrc could do anything when you cd into the directory. RVM prompts you to trust each .rvmrc file the first time you encounter it:
Type y to trust it. RVM remembers this choice and won't prompt again unless the file changes.
Most developers prefer .ruby-version files over .rvmrc because they're safer (just version numbers, not executable code) and work with multiple version managers, not just RVM.
Integrating with Bundler
Bundler handles gem dependencies for most Ruby projects. RVM works alongside Bundler, with each tool serving different purposes. RVM manages Ruby versions and environments, while Bundler manages gem dependencies within those environments.
Install Bundler in the global gemset so it's available everywhere:
The @global prefix tells RVM to use the global gemset for this command. Bundler is now accessible from any Ruby version and gemset.
Set up a new project. First, navigate out of the current directory:
Initialize Bundler:
Edit the Gemfile:
Install dependencies:
Bundler resolved dependencies, created Gemfile.lock, and installed gems. These gems went into your current RVM gemset (or the default gemset if you haven't specified one).
Run code using Bundler's environment:
Instead of just printing the version, Sinatra started a web server. This happens because requiring Sinatra in a one-liner triggers its auto-run feature. Press Ctrl-C to stop the server. This demonstrates that bundle exec successfully loaded the gems from your Gemfile.lock and executed the code with those specific versions.
The bundle exec prefix ensures Ruby loads gems from Gemfile.lock, not other versions that might be installed in your gemset.
Create a convenience alias:
Now use be instead of typing bundle exec repeatedly:
When using Bundler, you typically don't need custom gemsets for each project. The default gemset works fine because Bundler provides the dependency isolation you need. Some developers still use gemsets for organizational purposes, perhaps separating work projects from personal projects. The combination of RVM for Ruby versions and Bundler for gem management handles most needs effectively.
Final thoughts
This guide explored RVM's comprehensive approach to Ruby environment management. You've learned installation procedures, Ruby version management, and gemset isolation.
RVM provides extensive tools and deep integration for developers managing complex Ruby setups. Its gemset system, automatic environment switching, and dependency handling offer capabilities beyond minimal version managers.
Explore RVM's extensive documentation at rvm.io for advanced features, troubleshooting guides, and configuration options. The site covers everything from basic workflows to sophisticated multi-user installations.
Thanks for reading!