Back to Scaling Ruby Applications guides

Beginner's Guide to RVM

Stanley Ulili
Updated on October 1, 2025

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:

 
gpg2 --keyserver keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB

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:

 
\curl -sSL https://get.rvm.io | bash -s stable

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:

 
source ~/.rvm/scripts/rvm

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:

 
rvm --version
Output
rvm 1.29.12 (latest) by Michal Papis, Piotr Kuczynski, Wayne E. Seguin [https://rvm.io]

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:

 
rvm list known

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 install 3.2.2
Output
...
ruby-3.2.2 - #validate archive
ruby-3.2.2 - #extract
ruby-3.2.2 - #validate binary
ruby-3.2.2 - #setup
ruby-3.2.2 - #gemset created /home/stanley/.rvm/gems/ruby-3.2.2@global
ruby-3.2.2 - #importing gemset /home/stanley/.rvm/gemsets/global.gems...........
ruby-3.2.2 - #generating global wrappers........
ruby-3.2.2 - #gemset created /home/stanley/.rvm/gems/ruby-3.2.2
ruby-3.2.2 - #importing gemsetfile /home/stanley/.rvm/gemsets/default.gems evaluated to empty gem list
ruby-3.2.2 - #generating default wrappers........

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:

 
rvm install 3.4.6

View installed versions:

 
rvm list
Output
 * ruby-3.2.2 [ x86_64 ]
=> ruby-3.4.6 [ x86_64 ]

# => - current
# =* - current && default
#  * - default

Symbols indicate status: => marks the current version, * shows the default, and =* means both current and default.

Switch between installed versions:

 
rvm use 3.2.2 
Output
/home/stanley/.rvm/gems/ruby-3.2.2

Verify the switch worked:

 
ruby --version
Output
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux]

Set your default Ruby version for new shells:

 
rvm --default use 3.4.6

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 use 3.4.6
 
rvm gemset create api_project
Output
ruby-3.4.6 - #gemset created /home/stanley/.rvm/gems/ruby-3.4.6@api_project
ruby-3.4.6 - #generating api_project wrappers........

RVM created a new gem directory and generated wrapper scripts for gem executables.

Activate this gemset:

 
rvm use 3.4.6@api_project
Output
Using /home/stanley/.rvm/gems/ruby-3.4.6 with gemset api_project

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:

 
gem install sinatra
Output
...
Successfully installed sinatra-4.1.1
1 gem installed

List gemsets for your current Ruby:

 
rvm gemset list
Output
gemsets for ruby-3.4.6 (found in /home/stanley/.rvm/gems/ruby-3.4.6)
   (default)
=> api_project
   global

The arrow shows which gemset is active. Switch to the default gemset:

 
rvm use 3.4.6@default
 
gem list sinatra
Output
*** LOCAL GEMS ***

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:

 
rvm use 3.4.6@global
 
gem install bundler

Now bundler is accessible from any gemset under Ruby 3.4.6 without installing it repeatedly.

Remove gemsets you no longer need:

 
rvm gemset delete api_project

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:

 
mkdir blog_app
 
cd blog_app

Specify the required Ruby version:

 
echo "3.4.4" > .ruby-version

Leave the directory and return:

 
cd ..
 
cd blog_app
Output
Required ruby-3.4.4 is not installed.
To install do: 'rvm install "ruby-3.4.4"'

RVM detected the .ruby-version file but can't switch because Ruby 3.4.4 isn't installed yet. Install it:

 
rvm install 3.4.4

Specify a gemset as well:

 
echo "blog" > .ruby-gemset
 
cd ..
 
cd blog_app
Output
ruby-3.4.4 - #gemset created /home/stanley/.rvm/gems/ruby-3.4.4@blog
ruby-3.4.4 - #generating blog wrappers..............

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:

 
echo "rvm use 3.4.4@blog --create" > .rvmrc

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:

 
cd ..
 
cd blog_app
Output
==============================================================================
= NOTICE                                                                     =
==============================================================================
= RVM has encountered a new or modified .rvmrc file in the current directory =
= This is a shell script and therefore may contain any shell commands.       =
=                                                                            =
= Examine the contents of this file carefully to be sure the contents are   =
= safe before trusting it! ( Choose v[iew] below to view the contents )     =
==============================================================================
Do you wish to trust this .rvmrc file? (/home/stanley/blog_app/.rvmrc)
y[es], n[o], v[iew], c[ancel]>

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:

 
rvm @global do gem install bundler

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:

 
cd ..
 
mkdir sinatra_api
 
cd sinatra_api
 
echo "3.4.4" > .ruby-version

Initialize Bundler:

 
bundle init
Output
Writing new Gemfile to /home/stanley/sinatra_api/Gemfile

Edit the Gemfile:

Gemfile
source "https://rubygems.org"

gem "sinatra", "~> 4.0"
gem "puma", "~> 6.0"
gem "rackup", "~> 2.0"

Install dependencies:

 
bundle install
Output
Fetching gem metadata from https://rubygems.org/....
Resolving dependencies...
Fetching base64 0.3.0
Fetching logger 1.7.0
Installing base64 0.3.0
Installing logger 1.7.0
...
Fetching rack-protection 4.1.1
Installing rack-protection 4.1.1
Fetching rack-session 2.1.1
Installing rack-session 2.1.1
Fetching rackup 2.2.1
Installing rackup 2.2.1
Fetching sinatra 4.1.1
Installing sinatra 4.1.1
Fetching puma 6.6.1
Installing puma 6.6.1 with native extensions
Bundle complete! 3 Gemfile dependencies, 13 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

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:

 
bundle exec ruby -r sinatra -e "puts Sinatra::VERSION"
Output
== Sinatra (v4.1.1) has taken the stage on 4567 for development with backup from Puma
Puma starting in single mode...
* Puma version: 6.6.1 ("Return to Forever")
* Ruby version: ruby 3.4.6 (2025-09-16 revision dbd83256b1) +PRISM [x86_64-linux]
*  Min threads: 0
*  Max threads: 5
*  Environment: development
*          PID: 37758
* Listening on http://127.0.0.1:4567
* Listening on http://[::1]:4567
Use Ctrl-C to stop

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:

 
echo 'alias be="bundle exec"' >> ~/.bashrc
 
source ~/.bashrc

Now use be instead of typing bundle exec repeatedly:

 
be ruby -v
Output
ruby 3.4.6 (2025-09-16 revision dbd83256b1) +PRISM [x86_64-linux]

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!

Got an article suggestion? Let us know
Next article
Top 10 Ruby on Rails Alternatives for Web Development
Discover the top 10 Ruby on Rails alternatives. Compare Django, Laravel, Express.js, Spring Boot & more with detailed pros, cons & features.
Licensed under CC-BY-NC-SA

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.