Mise article cover image
Figure 1. Mise Cover Image

Managing different versions of programming languages and command-line tools across projects is a recurring pain. One project needs Node 18, another Node 22, a third pins Python 3.11 while your system ships 3.12. Traditionally we solved this with a zoo of single-purpose version managers like nvm, pyenv, rbenv, sdkman or asdf.

mise (pronounced "meez", short for mise-en-place) replaces all of them with a single, fast tool written in Rust. It manages tool versions, per-project environment variables and project tasks – roughly asdf + direnv + a task runner in one binary.

In the following tutorial I’m going to demonstrate how to install mise, manage tool versions per project, define environment variables and run project tasks.

Prerequisites

mise ships as a single binary and can be installed in several ways. The quickest is the install script:

Installing mise
curl https://mise.run | sh

Alternatively, use your package manager:

Installing mise with Homebrew
brew install mise

After installing, activate mise in your shell so it can manage your PATH. Add the appropriate line to your shell config:

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

Restart your shell and verify the installation:

Verifying the installation
mise --version

Output

Running the command above should produce a similar output:

2026.8.14 macos-arm64 (2026-08-25)

Managing Tool Versions

mise stores tool configuration in a mise.toml file at the root of your project. The cleanest way to add a tool is with mise use, which installs the tool and writes it to the config in one step.

Adding tools to a project
mise use node@22
mise use python@3.12
mise use rust@1.86

This creates or updates a mise.toml in the current directory:

mise.toml
[tools]
node = "22"
python = "3.12"
rust = "1.86"

As soon as you cd into the directory, mise activates exactly these versions. Leaving the directory restores your previous environment.

We can inspect which tools are active in the current directory:

Listing active tools
mise ls --current

Output

Running the command above should produce a similar output:

node    22.23.2  ~/project/mise.toml  22
python  3.12.9   ~/project/mise.toml  3.12
rust    1.86.0   ~/project/mise.toml  1.86

To install every tool declared in a project’s config in one go – for example after cloning a repository – simply run:

Installing all configured tools
mise install

Pinning Exact Versions

The example above uses fuzzy versions like node = "22", which resolve to the latest matching release. For reproducible builds you often want an exact version pinned across the whole team:

mise.toml
[tools]
node = "22.14.0"
python = "3.12.9"

You can also set a global default that applies whenever no project config is found:

Setting a global tool version
mise use --global node@lts

Managing Environment Variables

mise can load environment variables per project, replacing tools like direnv. Variables are declared in the [env] section of mise.toml and are only set while you are inside the project directory.

mise.toml
[tools]
node = "22"

[env]
NODE_ENV = "development"
DATABASE_URL = "postgres://localhost:5432/myapp"
_.file = ".env"

The special _.file directive loads additional variables from a dotenv file – handy for secrets you don’t want to commit. mise also supports composing variables and prepending to the PATH:

mise.toml with PATH manipulation
[env]
BIN_DIR = "{{config_root}}/bin"
_.path = ["{{config_root}}/bin", "{{config_root}}/node_modules/.bin"]

We can verify that the variables are set inside the project:

Checking loaded environment variables
mise env

Output

Running the command above should produce a similar output:

export DATABASE_URL='postgres://localhost:5432/myapp'
export NODE_ENV=development
export PATH='/home/user/project/bin:/home/user/project/node_modules/.bin:...'

Running Project Tasks

Beyond tool and environment management, mise doubles as a lightweight task runner. Tasks are defined in the [tasks] section and executed with mise run.

mise.toml with tasks
[tools]
node = "22"

[tasks.build]
description = "Build the project"
run = "npm run build"

[tasks.test]
description = "Run the test suite"
run = "npm test"

[tasks.ci]
description = "Run build and tests"
depends = ["build", "test"]

Tasks run inside the fully configured environment, so the correct tool versions and environment variables are always in place. List the available tasks with:

Listing available tasks
mise tasks

Output

Running the command above should produce a similar output:

build  Build the project
ci     Run build and tests
test   Run the test suite

Then run a task – dependencies are resolved automatically:

Running a task
mise run ci

Output

Running the command above should produce a similar output:

[build] $ npm run build
[test] $ npm test
[build] ...
[build] Finished in 1.2s
[test] ...
[test] Finished in 3.4s
Finished in 4.6s

Migrating from asdf, nvm and Friends

Because mise is compatible with the asdf plugin ecosystem and understands existing version files, migration is usually painless. mise reads legacy files like .tool-versions, .nvmrc or .python-version out of the box, so an existing project often works without any changes.

To adopt mise gradually, you can enable reading of these idiomatic version files:

Reading legacy version files
[settings]
idiomatic_version_file_enable_tools = ["node", "python"]

When you’re ready, convert an existing .tool-versions to a native mise.toml and remove the old per-tool managers from your shell config.