$linuxjunkies
>

Use mise (or asdf) for Tool Versioning

Install mise (or asdf) to manage Node, Python, Ruby, and more from one .tool-versions file — replacing nvm, pyenv, and rbenv with a single fast tool.

BeginnerUbuntuDebianFedoraArch7 min readUpdated June 7, 2026

Before you start

  • A working terminal with bash, zsh, or fish
  • curl and git installed
  • Build tools (build-essential or Development Tools group) for compiled runtimes like Python and Ruby
  • Existing nvm/pyenv/rbenv removed or disabled to avoid PATH conflicts

Managing multiple versions of Node.js, Python, Ruby, or Go across projects is a common headache. Traditionally you'd reach for a separate tool per language — nvm for Node, pyenv for Python, rbenv for Ruby. mise (formerly rtx, a fast Rust rewrite inspired by asdf) handles all of them through a single interface with a shared .tool-versions file. asdf is the older, battle-tested original written in bash; both read the same config format, so switching is painless. This guide covers mise primarily, with asdf notes where they differ.

Why mise over nvm/pyenv/rbenv

Each per-language version manager adds shell hooks, PATH manipulations, and separate config files. mise replaces all of them with one binary, one config format, and hook injection that is measurably faster than asdf's shim approach. It activates versions lazily per-directory, so your shell startup stays quick.

  • One .tool-versions file per project (or a global fallback in ~/.tool-versions)
  • Hundreds of plugins available through the asdf plugin ecosystem
  • Drop-in compatible with existing asdf setups — reads the same config files
  • Written in Rust; activation adds roughly 2–5 ms to shell startup vs 30–100 ms for asdf shims

Install mise

The upstream script installs a static binary to ~/.local/bin/mise. Review it first if that is your policy.

curl https://mise.run | sh

Alternatively, install via your package manager:

# Debian/Ubuntu (via apt)
apt install -y gpg
curl -fsSL https://mise.jdx.dev/gpg-key.pub | gpg --dearmor | sudo tee /usr/share/keyrings/mise-archive-keyring.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/mise-archive-keyring.gpg arch=amd64] https://mise.jdx.dev/deb stable main" | sudo tee /etc/apt/sources.list.d/mise.list
sudo apt update && sudo apt install -y mise
# Fedora / RHEL / Rocky
dnf install -y dnf-plugins-core
dnf config-manager --add-repo https://mise.jdx.dev/rpm/mise.repo
dnf install -y mise
# Arch Linux (AUR)
yay -S mise-bin
# or with paru
paru -S mise-bin

Activate mise in your shell

Add the activation line to your shell's rc file. This sets up PATH and the directory-change hook that swaps versions automatically.

# bash — add to ~/.bashrc
echo 'eval "$(mise activate bash)"' >> ~/.bashrc
source ~/.bashrc
# zsh — add to ~/.zshrc
echo 'eval "$(mise activate zsh)"' >> ~/.zshrc
source ~/.zshrc
# fish — add to ~/.config/fish/config.fish
echo 'mise activate fish | source' >> ~/.config/fish/config.fish
source ~/.config/fish/config.fish

Install asdf (optional, if you prefer the original)

Skip this section if you chose mise. asdf requires git and a supported shell.

git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.14.1
# bash — add to ~/.bashrc
echo '. "$HOME/.asdf/asdf.sh"' >> ~/.bashrc
source ~/.bashrc

asdf uses shims — small wrapper scripts in ~/.asdf/shims/ — rather than PATH rewriting. After installing a tool version you must run asdf reshim <plugin> to generate them.

Add plugins and install tool versions

Finding and adding a plugin

mise bundles first-party support for the most common runtimes (node, python, ruby, go, java, rust via rustup). For others, use the asdf plugin registry.

# List bundled/known tools
mise registry
# Install a specific Node.js version globally
mise use --global node@22

# Install Python 3.12 globally
mise use --global [email protected]

# Install Ruby 3.3
mise use --global [email protected]

For a plugin not bundled natively, add the asdf plugin URL directly:

mise plugins add terraform https://github.com/asdf-community/asdf-hashicorp.git
mise use --global [email protected]
# asdf equivalent
asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git
asdf install nodejs 22.4.0
asdf global nodejs 22.4.0

The .tool-versions file

Drop a .tool-versions file in any project root. Both mise and asdf read it and activate the listed versions when you enter that directory.

cat .tool-versions
# node 22.4.0
# python 3.12.3
# ruby 3.3.1

Create or update it with mise use (no --global flag) from inside your project directory:

cd ~/projects/myapp
mise use [email protected]
mise use [email protected]

This writes the versions into the local .tool-versions. Commit it to version control so teammates get the same environment automatically — as long as they also have mise or asdf installed.

mise also supports its own mise.toml format (richer, supports environment variables and tasks), but .tool-versions is the portable choice when collaborating with asdf users.

Replacing nvm, pyenv, and rbenv

If you already use per-language managers, remove their shell hooks first to avoid conflicts.

Remove nvm

# Remove nvm lines from ~/.bashrc or ~/.zshrc, then:
rm -rf ~/.nvm

Remove pyenv

# Remove the pyenv init block from your rc file, then:
rm -rf ~/.pyenv

Remove rbenv

# Remove rbenv init from your rc file, then:
rm -rf ~/.rbenv

After removing each tool, reinstall the versions you need through mise:

mise use --global node@22 [email protected] [email protected]

Verify the setup

mise doctor

This checks activation, PATH, and plugin health. Fix any warnings it reports before continuing.

mise list

Output shows installed tools, their versions, and whether each is active (marked with a *). Example output (yours will differ):

# node    22.4.0   ~/.tool-versions
# python  3.12.3   ~/projects/myapp/.tool-versions
# ruby    3.3.1    ~/.tool-versions
node --version
python --version
ruby --version

Keeping tools updated

# See newer versions available for installed tools
mise outdated

# Upgrade a specific tool
mise upgrade node

# Upgrade all tools to latest within their declared constraints
mise upgrade

Troubleshooting

Command not found after installing a tool

If you used asdf, run asdf reshim <plugin> <version> to regenerate shims. With mise this is rare — if it happens, reload your shell with exec $SHELL and check mise doctor for PATH issues.

Wrong version activated

Run mise current to see which version is active and which file set it. A closer .tool-versions (or mise.toml) in a parent directory always wins. Check for a stale global override with mise list.

Build failures during install

Python and Ruby compile from source by default. Missing build dependencies are the most common cause. Install them first:

# Debian/Ubuntu
sudo apt install -y build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev
# Fedora/RHEL
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y openssl-devel zlib-devel bzip2-devel readline-devel sqlite-devel

Conflicts with system Python

mise-managed Python is only active in your shell session and directories where it is declared. System Python (used by the OS and system pip) is untouched. Never run mise use --global python on a system where OS tools depend on a specific Python — set it per-project instead.

tested on:Ubuntu 24.04Fedora 40Arch rollingDebian 12

Frequently asked questions

Can I use mise and asdf on the same machine?
Technically yes — they read the same .tool-versions files — but running both simultaneously causes PATH conflicts. Pick one and remove the other's shell activation lines.
Does mise work with Wayland / GUI apps that need a specific runtime?
mise only activates versions inside your shell session. GUI apps launched outside a terminal (e.g. from a desktop launcher) don't inherit mise's PATH, so they fall back to the system-installed version. Use a wrapper script or a systemd user service that sources mise for GUI use cases.
Is .tool-versions enough, or should I use mise.toml?
.tool-versions is portable across both mise and asdf, which is ideal for shared projects. mise.toml adds support for environment variables, tasks, and version constraints, and is the better choice for mise-only projects.
Will mise slow down my shell startup?
mise uses runtime PATH activation rather than shims, adding roughly 2–5 ms. That is noticeably faster than asdf's shim approach, which can add 30–100 ms on slower hardware.
How do I set a version for just one command without changing .tool-versions?
Use mise exec: for example, `mise exec [email protected] -- python myscript.py` runs the script under Python 3.11 without affecting the directory's declared version.

Related guides