My Ruby version management has been on a bit of a journey. It started with rbenv - I’ve written a few posts on it - then for a while I just installed Ruby straight from Homebrew (fine until two projects disagree), then asdf, which was the first tool to manage everything with one plugin system. Earlier this year I rebuilt my dotfiles and landed on mise, and this feels like the final stop. It’s asdf’s idea done properly - same .tool-versions compatibility, but a single fast binary, no plugins to install, and every version pinned in a config file that lives in my dotfiles repo.

Installing

curl https://mise.run | sh

Then activate it in your shell by adding this to the bottom of ~/.zshrc:

eval "$(~/.local/bin/mise activate zsh)"

Open a new terminal and mise --version should answer.

Ruby, globally

mise use --global [email protected]

which ruby   # should be a mise shim path, NOT /usr/bin/ruby
ruby -v

That which ruby check matters - if it comes back with the system Ruby, mise isn’t activated in your shell yet. I learned this the hard way when my machine-setup script installed everything except the Ruby bits, because a freshly opened terminal mid-install didn’t have the shims in its PATH. If you’re scripting a setup, put the shims in PATH explicitly rather than relying on shell activation:

export PATH="$HOME/.local/bin:$HOME/.local/share/mise/shims:$PATH"

Rails on top

Once which ruby points at mise, gems work exactly as before:

gem install rails jekyll bundler
rails -v

No rbenv rehash dance - mise’s shims just pick new executables up.

Per-project versions

This is where it earns its keep. Inside any project:

That writes a .mise.toml into the directory, and from then on cd-ing in switches Ruby automatically. It also reads plain .ruby-version files, so every existing project keeps working untouched.

Pin it in your dotfiles

My global versions live in ~/.config/mise/config.toml, which is stowed from my dotfiles repo:

[tools]
ruby = "4.0.6"
node = "24"

Upgrading a tool is now: edit that file, run mise install, commit. On any other machine it’s git pull && mise install and everything matches. No hunting through shell scripts for where a version was hardcoded.

The everyday commands

mise ls              # everything installed
mise ls ruby         # just Ruby versions
mise install         # install whatever the config asks for
mise upgrade         # upgrade mise itself
mise doctor          # when something feels off

One tool, one config file, every language. If you’re still juggling rbenv and nvm side by side, this is your sign. And if you want to see where that config.toml lives and how the whole machine gets rebuilt around it, that’s covered in the dotfiles post.