Ruby with Mise
Mise is multi-language version manager for Mac, handling Ruby and other languages from a single configuration file. Use Mise to install and switch between Ruby versions, manage gems with Bundler, and configure Rails projects with both Ruby and Node.js.
Mise is a multi-language version manager that handles Ruby, Node.js, Python, Java, and more from a single command line tool and configuration file. Mise unifies version management across your development environment. It is especially useful for Rails development because older Rails projects need both Ruby and Node.js. See Mise on Mac to understand how mise works and why developers switch from Ruby-specific managers like rbenv, rvm, or chruby. For a broader overview of Ruby installation options on Mac, see Ruby on Mac.
Managing Ruby versions is one step in setting up your Mac for development. See the full Mac setup roadmap.
Before you get started
You'll need a terminal application to install and use the mise version manager for Ruby. Apple includes the Mac terminal but I prefer Warp Terminal. Warp is an easy-to-use terminal application, with AI assistance to help you learn and remember terminal commands. Download Warp Terminal now; it's FREE and worth a try.
First steps for Ruby with Mise
If you haven't installed mise yet, see the Install Mise on Mac guide. Verify that mise is working by running mise --version in your terminal. You should also run mise doctor after installation to ensure mise is activated by the ~/.zshrc file and shell hooks load correctly. This allows mise to automatically switch Ruby versions and manage environment variables when you enter directories with a .mise.toml or .ruby-version file.
Set a global Ruby version
To set a default Ruby version for your entire system, run mise use with the --global flag. This version applies whenever you're in a directory without a .mise.toml file.
$ mise use --global [email protected]
The global version is stored in ~/.config/mise/config.toml. You can have multiple project-specific versions and still maintain a system-wide default. Mise downloads the latest precompiled Ruby binary and installs it to ~/.local/share/mise/installs/ruby/.
Project configuration files
You'll need a .mise.toml file in your project root to specify the exact Ruby version your project requires. This file enables automatic version switching: whenever you navigate into the project directory, mise activates the configured version. Commit this file to version control so your team uses identical Ruby versions. See Mise Configuration on Mac for other settings including environment variables, automated tasks, and advanced setup.
Existing projects with Mise for Ruby
If you have an existing Ruby project, you can use mise edit to create a .mise.toml configuration file. Instead of writing a file by hand, mise edit launches a terminal-based interactive editor (a TUI) that knows the structure of mise configuration and can search the tool registry for you. If your project already has a .ruby-version file, mise reads it automatically, and you can import it into the editor.
New projects with Mise for Ruby
If you are starting a new Ruby project, open your terminal application and navigate to your project directory. Run the command to install the latest Ruby version.
$ mise use ruby
If you haven't installed a global Ruby version yet, Mise downloads the latest precompiled binary and installs it to ~/.local/share/mise/installs/ruby/. The command creates a .mise.toml file in your current directory with the installed version.
The configuration file is simple:
[tools]
ruby = "latest"
Verify the installation by checking the Ruby version and confirming gem functionality.
$ ruby --version
$ gem --version
Both commands should display version numbers. The gem tool is bundled with your Ruby installation.
Install a specific Ruby version
If your project requires a particular major or minor version, use fuzzy matching to let mise select the latest patch version.
$ mise use [email protected]
This installs the latest Ruby 4.0.x version. To pin an exact version, use the --pin flag.
$ mise use --pin [email protected]
The --pin flag records the complete version string in .mise.toml, ensuring consistent behavior across your team.
Discover available Ruby versions
Before installing, you can browse all available Ruby versions using mise ls-remote ruby. This command shows every version available for installation.
$ mise ls-remote ruby
The output lists versions from oldest to newest. You can filter by major version to see all releases within a series.
$ mise ls-remote [email protected]
This displays every 4.0.x release available, helping you identify stable and recent patch versions.
Choose the right Ruby version
Ruby uses a versioning scheme inspired by semantic versioning (though it doesn't always follow the SemVer spec). The currently supported Ruby versions are 3.2, 3.3, 3.4, and 4.0, with 3.0 and 3.1 already end-of-life in 2026.
Ruby 4.0 is recommended for new projects and Rails development. It includes performance improvements and modern language features. Ruby 3.4 remains widely used for projects that haven't migrated yet.
Handle .ruby-version files
If you're migrating from the Ruby-only rbenv version manager or have projects using .ruby-version files, mise reads them automatically. No conversion is necessary.
Mise recognizes several legacy version file formats without any configuration:
- .ruby-version (rbenv format)
- .tool-versions (asdf format)
When mise finds any of these files, it automatically switches to the specified version upon entering the directory. To ensure .ruby-version compatibility is fully enabled, run this command once.
$ mise settings add idiomatic_version_file_enable_tools ruby
This enables automatic version switching for all version file formats. After running this, your existing rbenv setups work seamlessly with mise. But you might want to use a .mise.toml file because it gives you more options, as described in Mise Configuration.
Migrate from rbenv to mise
If you currently use rbenv, switching to mise takes just a few steps. Your existing Ruby installations don't need to be reinstalled immediately.
You can install Ruby globally with mise use -g [email protected] (using the -g flag) or install Ruby versions with mise in each of your projects:
$ mise use [email protected]
Then comment out the rbenv initialization lines in your shell configuration file. If you use zsh, open ~/.zshrc and find lines that source rbenv (usually near the end). Comment them out or delete them, then save the file.
After restarting your terminal, mise handles Ruby version management instead of rbenv. Your existing .ruby-version files work automatically with mise.
Switch between Ruby versions per project
One of mise's most powerful features is automatic version switching. When you navigate into a project directory with a .mise.toml or .ruby-version file, mise automatically switches to that project's Ruby version.
Create two project directories with different Ruby versions to see this in action. In the first project, create a .mise.toml specifying Ruby 4.0.
$ cd project-a/
$ mise use [email protected]
$ ruby --version
ruby 4.0.x
Create a second project directory and install Ruby 3.2.
$ cd ../project-b/
$ mise use [email protected]
$ ruby --version
ruby 3.2.x
Now navigate back to project-a. Mise automatically switches the version.
$ cd ../project-a/
$ ruby --version
ruby 4.0.x
This automatic switching happens whenever you enter a directory containing version specifications, making it impossible to accidentally use the wrong Ruby version.
Follow best practices for Ruby versions
When managing Ruby versions across your projects, follow these practices to prevent conflicts and maintain consistency.
Always specify Ruby versions in .mise.toml for projects you plan to share with others or maintain long-term. This ensures new team members and future versions of yourself use identical configurations.
Use exact version pinning for production projects. The --pin flag creates reproducible builds across environments.
$ mise use --pin [email protected]
For monorepos with multiple packages, each subdirectory can have its own .mise.toml. Mise automatically switches versions when entering different subdirectories.
Keep your global Ruby version at a reasonable stable release that matches your most common work. This provides a sensible fallback when you're outside project directories.
Regular updates to Ruby versions are important for security. Patch releases address vulnerabilities. See Update Ruby on Mac for guidance on keeping Ruby current. Review Ruby release notes before updating to new major versions, as breaking changes sometimes require code updates.
Set default gems with Mise
Mise can automatically install default gems for any project whenever a new Ruby version is installed. Create a file at ~/.default-gems with one gem per line.
bundler
solargraph
rubocop
rails
Each time mise installs a Ruby version, these gems are installed automatically for that version. This eliminates manual gem installation after version switches.
Use different Ruby versions for different tasks
Advanced workflows sometimes require different Ruby versions for different purposes. Mise supports executing specific commands with non-default versions.
Use mise exec to run a single command with a specific Ruby version without changing your default.
$ mise exec [email protected] -- ruby --version
ruby 3.2.x
This approach is useful for testing compatibility with older versions or running tools that require specific Ruby versions without permanently switching.
Verify your setup
After configuring mise for Ruby, verify the setup is complete and working correctly.
Check that mise is recognizing your .mise.toml configuration.
$ mise current ruby
This displays the current Ruby version mise is using. It should match your .mise.toml specification.
Gems, Bundler, and Mise
Ruby has its own package system called RubyGems. Ruby software libraries are distributed as gems. You will encounter gems such as rails (the web framework), rspec (testing), and rubocop (code linting). You can install any gem with the gem install command, but you almost never should: installing gems globally causes version conflicts between projects.
Instead, Ruby applications use Bundler to manage dependencies. Bundler reads a file called Gemfile in your project directory, downloads the gems listed there, and records the exact versions in Gemfile.lock. This keeps every developer and every deployment on the same versions.
A typical Gemfile looks like this:
source "https://rubygems.org"
gem "rails"
gem "pg"
gem "puma"
Install dependencies with:
$ bundle install
Mise manages Ruby versions. Bundler manages gems. The two tools work at different levels:
- Mise installs and activates the correct Ruby version for your project
- Bundler installs and locks the gems your project requires
A typical Ruby workflow looks like this:
- Set the Ruby version in
.mise.toml:
[tools]
ruby = "4.0"
- Install the Ruby runtime:
$ mise install
- Install project dependencies:
$ bundle install
- Run your application:
$ bundle exec rails server
This layered approach keeps responsibilities clear. Mise handles the runtime, Bundler handles the libraries, and you avoid the version conflicts that make Ruby development frustrating. But you can do better: you can eliminate bundle exec entirely.
Automate bundle exec
Gems that are used as commands should be run using bundle exec:
$ bundle exec rails server
However, typing bundle exec before every command is one of Ruby's most annoying habits. Bundler requires it so commands load the correct gem versions, but it clutters every workflow. Mise can make it disappear.
The trick is binstubs. When you run bundle install --binstubs, Bundler generates small wrapper scripts in your project's bin/ directory: bin/rails, bin/rspec, bin/rubocop, and so on. Each wrapper loads the correct bundle automatically, so you do not need the bundle exec prefix.
Generate binstubs from your project root:
$ bundle install --binstubs
Then configure .mise.toml to add bin/ to your PATH:
[tools]
ruby = "4.0"
[env]
_.path = ["./bin"]
Mise activates this PATH when you enter the project directory. Now your day-to-day commands look like this:
$ rails server
$ rspec
$ rubocop
Each command resolves to the binstub in bin/, which handles the Bundler setup internally. No bundle exec required.
Rails projects already follow this convention: Rails generates a bin/ directory by default. Adding _.path to your .mise.toml extends the same approach to every Ruby project.
For one-off commands where you do not have a binstub, use mise x instead of bundle exec:
$ mise x -- rake db:migrate
The mise x command reads your .mise.toml, activates the correct Ruby and PATH, and runs the command in the project context.
You can also define tasks in .mise.toml for commands you run frequently:
[tasks.server]
run = "bin/rails server"
description = "Start the Rails dev server"
[tasks.test]
run = "bin/rspec"
description = "Run the test suite"
Then run them with:
$ mise run server
$ mise run test
The mental model is simple: Mise activates Ruby, binstubs activate Bundler, and tasks activate workflows. The bundle exec prefix quietly disappears from your daily routine.
Compare rv and Mise
rv is a Ruby-focused tool created by André Arko, one of the Bundler maintainers. It is written in Rust and aims to combine Ruby version management, dependency management, and command execution into a single tool.
Mise is the better choice for most developers right now. Mise handles Ruby, Node.js, Python, Java, and dozens of other runtimes from a single configuration file. If you work with more than one language (and most developers do), Mise gives you one tool for everything.
The tool rv is still evolving and may eventually become a companion to Mise rather than a replacement. Because rv focuses specifically on the Ruby toolchain, it could handle Ruby-specific concerns (such as dependency resolution) while Mise manages the broader development environment. You can already install rv with Mise:
$ mise install rv
For now, use Mise and Bundler. It is the setup that works with every Ruby tutorial and deployment system you will encounter. Keep an eye on rv as it matures.
Rails with Mise
Rails projects fall into two categories when it comes to JavaScript. Newer apps can skip Node.js entirely by using importmaps. Older apps (or those with complex JavaScript needs) use a Node-based bundler such as esbuild, webpack, or rollup. Mise can handle both setups.
Use importmaps (no Node.js required)
Rails 7, released in December 2021, introduced importmap-rails as the default JavaScript approach. This was a breakthrough: for the first time, Rails applications could eliminate the complex JavaScript build toolchain that had been required since the Webpacker era. Importmaps let the browser load ES modules directly, so there is no bundling step, no Node.js dependency, and no package.json to manage.
If your app uses only modest JavaScript (Stimulus, Turbo, a few libraries), importmaps are all you need. In this setup, your .mise.toml only needs Ruby:
[env]
RAILS_ENV = "development"
[tools]
ruby = "4.0.1"
Rails still compiles assets such as CSS and images, but JavaScript loads via import maps and does not depend on Node at all.
Use a JavaScript bundler with Node.js
Many developers work on legacy Rails applications that still require complex JavaScript tooling. Mise is ideal for this case because it keeps Ruby and Node.js versions aligned in a single configuration file. If your project uses TypeScript, React, Vue, JSX, or a bundler such as esbuild, rollup, or webpack through jsbundling-rails, you need Node.js alongside Ruby. These tools compile and bundle your JavaScript into app/assets/builds, which the Rails asset pipeline then serves.
Keep both Ruby and Node in your .mise.toml:
[env]
RAILS_ENV = "development"
[tools]
ruby = "4.0.1"
node = "24.0.0"
When you enter the project directory, Mise automatically activates both Ruby 4.0.1 and Node.js 24.0.0. Node (and a package manager such as npm, Yarn, or pnpm) installs your JavaScript dependencies from package.json and runs scripts such as npm run build during development and asset precompilation.
See the tutorial Node.js with Mise to learn how to set up Node.js with Mise.
For a Rails project with JavaScript tooling, you can define common operations in .mise.toml so the entire team runs them the same way:
[tasks.setup]
run = "bundle install && npm install"
description = "Install Ruby and JavaScript dependencies"
[tasks.dev]
run = "bin/dev"
description = "Start the Rails dev server"
[tasks.test]
run = "bin/rspec"
description = "Run the test suite"
Run mise run setup to install all dependencies. Run mise run dev to start the development server. With Mise managing Ruby, Node, and task definitions in a single file, every developer on the team gets an identical environment.
Ruby build dependencies
Mise defaults to downloading precompiled Ruby binaries, eliminating the need for build dependencies in most scenarios. Precompiled binaries install instantly and avoid common compilation errors.
Build dependencies are needed only when:
- A precompiled binary is unavailable for your Ruby version
- You're installing an older Ruby version without binary support
- You disable precompiled binary mode
For modern Ruby versions (3.1+), precompiled binaries are almost always available. If you need to compile Ruby from source, install these Homebrew packages.
$ brew install openssl@3 libyaml gmp rust
Each dependency serves a specific purpose. openssl@3 provides SSL/TLS support, libyaml enables YAML parsing, gmp provides mathematical functions, and rust is needed for some Ruby extensions.
Resolve OpenSSL compilation errors
Ruby compilation sometimes conflicts with OpenSSL versions. If you encounter OpenSSL errors when compiling Ruby, try using precompiled binaries first.
If precompiled binaries aren't available, configure the compiler to use OpenSSL 3 explicitly.
$ export LDFLAGS="-L/opt/homebrew/opt/openssl@3/lib"
$ export CPPFLAGS="-I/opt/homebrew/opt/openssl@3/include"
$ mise install [email protected]
These environment variables direct Ruby's build system to your Homebrew OpenSSL installation, resolving library path conflicts.
Troubleshoot Ruby issues
If version switching isn't working, verify that mise's shell hooks are loaded. Restart your terminal completely (close all tabs) to ensure the latest shell configuration is loaded. Run mise doctor for diagnostics to ensure mise is activated by the ~/.zshrc file.
Check that .mise.toml exists in your project directory and contains valid configuration.
$ cat .mise.toml
The file should have a [tools] section with ruby = "version". If the file is missing, run mise use ruby@version to create it.
If gems aren't found after switching Ruby versions, reinstall them for the current version.
$ bundle install
If Bundler reports gem conflicts after a Ruby version upgrade, the Gemfile.lock might be outdated. Delete it and regenerate it.
$ rm Gemfile.lock
$ bundle install
Bundler resolves dependencies for your current Ruby version and creates a fresh Gemfile.lock.
Set up IDE integration
Popular Ruby IDEs automatically detect mise-installed Ruby versions. RubyMine and VSCode both recognize mise installations alongside rbenv and RVM installations.
If your IDE doesn't automatically detect Ruby, you can manually configure the interpreter path. In RubyMine or VSCode, point to the mise installation directory.
The path is: ~/.local/share/mise/installs/ruby/version/bin/ruby
Replace "version" with your specific Ruby version. This allows IDEs to use mise-managed Ruby for debugging, linting, and other development tools.
Continue setting up your Mac
Don't miss the full visual roadmap and checklist that shows how to set up a Mac for software development, with all the essential tools and settings you might not yet know about.