Managing runtime versions is one of those things you don’t think about much — until you switch projects and something breaks. For a long time, I used rbenv and nvm to handle the versions of Ruby and Node.js on this project. Each worked fine on its own, but the friction was always there. Two separate tools, two config files, and two things to remember when getting back to the project. Something was feeling off.
And then I found mise, and I had one of those aha moments where everything falls into place.
The problem with multiple version managers
If you work across different languages, you probably have a setup that looks something like this:
~/.rbenv+rbenvshell integration +.ruby-versionper project~/.nvm+nvmshell integration +.nvmrcper project- Maybe
pyenvfor Python, orgoenvfor Go…
Each version manager has its own installation method, its own shell hook, its own version file format, and its own quirks. When you clone a new repo, you need to check which version files are present and install the right runtimes manually. It works, but it’s friction. And that’s one of the problems that mise tries to solve.
What is mise
mise (pronounced “meez”) is a polyglot version manager. It replaces rbenv, nvm, pyenv, goenv, and similar tools with a single binary. It reads a .mise.toml file in your project root and automatically activates the correct versions when you cd into the directory.
Key features:
- One config file:
.mise.tomllists all your project’s runtimes and versions - Automatic switching: mise activates the right versions as you navigate between projects
- Wide language support: Ruby, Node.js, Python, Go, Rust, and many more
- No shell plugins needed: uses a shell hook (
eval "$(mise activate bash/zsh)") instead of per-tool plugins
Before and after
Here’s what my setup looked like before:
# Before: .ruby-version
ruby 3.3.4
# Before: .nvmrc
22.2.0
Two files, two tools, two shell integrations.
After switching to mise, everything lives in one file:
# After: .mise.toml
[tools]
ruby = "3.3.4"
node = "22.2.0"
This is the actual .mise.toml file from this blog’s repository.
Migration
Migrating was straightforward. Here’s what I did:
1. Install mise
curl https://mise.run | sh
Then add the shell hook to your .zshrc (or .bashrc) :
eval "$(~/.local/bin/mise activate zsh)"
2. Configure your tools
Create a .mise.toml file in your project root with the runtimes you need:
# mise.toml
[tools]
ruby = "3.3.4"
node = "22.2.0"
Then run mise install to download and install the versions specified in the file. mise reads the config automatically when you cd into the project directory.
3. Remove the old managers
Once everything works with mise, you can remove rbenv and nvm:
# Remove rbenv
rm -rf ~/.rbenv
# Remove rbenv from .zshrc / .bashrc
# Remove nvm
rm -rf ~/.nvm
# Remove nvm from .zshrc / .bashrc
Don’t forget to also delete the old .ruby-version and .nvmrc files from your projects — mise uses .mise.toml instead.
Conclusion
Switching from rbenv and nvm to mise took minutes, and removed a layer of friction I’d been tolerating for years. If your version manager setup feels heavier than it should, give mise a try.
Thanks for reading! If you found this post useful, consider buying me a coffee to support the blog. For questions or comments, feel free to reach out on X!
Until next time!