# 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)](https://rvm.io/) 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.

[ad-logs]

## 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](https://www.ruby-lang.org/en/documentation/installation/) 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:

```command
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:

```command
\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:

```command
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:

```command
rvm --version
```

```text
[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:

```command
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:

```command
rvm install 3.2.2
```

```text
[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:

```command
rvm install 3.4.6
```

View installed versions:

```command
rvm list
```

```text
[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:

```command
rvm use 3.2.2 
```

```text
[output]
/home/stanley/.rvm/gems/ruby-3.2.2
```

Verify the switch worked:

```command
ruby --version
```

```text
[output]
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux]
```

Set your default Ruby version for new shells:

```command
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:

```command
rvm use 3.4.6
```
```command
rvm gemset create api_project
```

```text
[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:

```command
rvm use 3.4.6@api_project
```

```text
[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:

```command
gem install sinatra
```

```text
[output]
...
Successfully installed sinatra-4.1.1
1 gem installed
```

List gemsets for your current Ruby:

```command
rvm gemset list
```

```text
[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:

```command
rvm use 3.4.6@default
```

```command
gem list sinatra
```

```text
[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:

```command
rvm use 3.4.6@global
```
```command
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:

```command
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:

```command
mkdir blog_app
```
```command
cd blog_app
```

Specify the required Ruby version:

```command
echo "3.4.4" > .ruby-version
```

Leave the directory and return:

```command
cd ..
```
```command
cd blog_app
```

```text
[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:

```command
rvm install 3.4.4
```

Specify a gemset as well:

```command
echo "blog" > .ruby-gemset
```

```command
cd ..
```
```command
cd blog_app
```

```text
[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:

```command
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:

```command
cd ..
```
```command
cd blog_app
```

```text
[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:

```command
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:

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

Initialize Bundler:

```command
bundle init
```

```text
[output]
Writing new Gemfile to /home/stanley/sinatra_api/Gemfile
```

Edit the `Gemfile`:

```ruby
[label Gemfile]
source "https://rubygems.org"

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

Install dependencies:

```command
bundle install
```

```text
[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:

```command
bundle exec ruby -r sinatra -e "puts Sinatra::VERSION"
```

```text
[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:

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

Now use `be` instead of typing `bundle exec` repeatedly:

```command
be ruby -v
```

```text
[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](https://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!