← All articles
TERMINAL mise: The Dev Tool Manager That Replaced asdf, nvm, ... 2026-02-14 · 6 min read · mise · asdf · version-manager

mise: The Dev Tool Manager That Replaced asdf, nvm, and pyenv

Terminal 2026-02-14 · 6 min read mise asdf version-manager nvm pyenv devtools terminal

mise: The Dev Tool Manager That Replaced asdf, nvm, and pyenv

mise (pronounced "meez") is a polyglot development tool manager written in Rust. It manages runtime versions (Node, Python, Ruby, Go, Java, and hundreds more), sets environment variables per project, and runs project tasks -- all from a single tool with a single config file.

mise development tool manager logo

If you are currently juggling nvm for Node, pyenv for Python, and rbenv for Ruby, mise replaces all three. If you are using asdf, mise is its faster, more capable successor that reads the same .tool-versions files.

Installation

# macOS
brew install mise

# Linux (recommended)
curl https://mise.run | sh

# Or via cargo
cargo install mise

After installation, activate mise in your shell:

# Add to ~/.bashrc or ~/.zshrc
eval "$(mise activate bash)"   # for bash
eval "$(mise activate zsh)"    # for zsh
eval "$(mise activate fish)"   # for fish

Restart your shell or source the config file.

Basic Usage: Managing Runtimes

Installing Tools

# Install a specific Node version
mise use [email protected]

# Install the latest Python 3.12.x
mise use [email protected]

# Install the latest Go
mise use go@latest

# Install globally (applies to all projects)
mise use --global node@22

mise use both installs the tool and sets it as active for the current directory. It updates your .mise.toml config file automatically.

Listing and Switching

# See what's installed
mise ls

# See available versions for a tool
mise ls-remote node | tail -20

# Switch versions
mise use node@20

# See which version is active and why
mise where node
mise which node

How It Works

When you run node, mise intercepts the call through a shim (or shell hook) and routes it to the correct version based on your project's config file. The lookup order is:

  1. .mise.toml in current directory (or parents)
  2. .tool-versions in current directory (or parents)
  3. ~/.config/mise/config.toml (global default)

This means each project can pin its own versions without affecting other projects.

Configuration: .mise.toml

The .mise.toml file is mise's native config format. It is more expressive than asdf's .tool-versions.

[tools]
node = "22"
python = "3.12"
bun = "latest"
terraform = "1.7"

[env]
DATABASE_URL = "postgres://localhost:5432/myapp_dev"
NODE_ENV = "development"
AWS_PROFILE = "myapp-dev"

# Load environment from a .env file
[env]
_.file = ".env"

[tasks.dev]
run = "bun run dev"
description = "Start development server"

[tasks.test]
run = "bun test"
description = "Run tests"

[tasks.lint]
run = "biome check ."
description = "Run linter"

[tasks.db-migrate]
run = "bun run drizzle-kit push"
description = "Run database migrations"
depends = ["db-check"]

[tasks.db-check]
run = "pg_isready -h localhost"
description = "Check database is running"

Environment Variables

mise manages environment variables per project, replacing direnv for many use cases:

[env]
# Static values
API_KEY = "dev-key-123"
PORT = "3000"

# Reference other env vars
HOME_BIN = "{{env.HOME}}/bin"

# Load from files
_.file = [".env", ".env.local"]

# Add to PATH
_.path = ["./node_modules/.bin", "./scripts"]

When you cd into a project directory, mise automatically sets these variables. When you leave, they are unset. No more leaking development credentials into unrelated projects.

Security note: mise will prompt for confirmation the first time it loads a .mise.toml that sets environment variables, preventing malicious repos from silently exporting variables.

Task Runner

mise includes a built-in task runner that replaces Makefiles, npm scripts, and Just for many workflows.

[tasks.build]
run = """
bun run build
cp -r dist/ deploy/
"""
description = "Build and prepare for deployment"

[tasks.deploy]
run = "rsync -avz deploy/ server:/var/www/app/"
depends = ["build", "test"]
description = "Deploy to production"

[tasks.clean]
run = "rm -rf dist/ deploy/ node_modules/.cache"
description = "Clean build artifacts"

Run tasks with:

mise run dev
mise run test
mise run deploy

# List available tasks
mise tasks

Tasks can depend on other tasks, accept arguments, and run in parallel:

[tasks.ci]
depends = ["lint", "test", "build"]
description = "Run full CI pipeline"

[tasks.test-unit]
run = "bun test --filter unit"

[tasks.test-integration]
run = "bun test --filter integration"

[tasks.test]
depends = ["test-unit", "test-integration"]
wait_for = ["db-check"]

Tasks vs npm Scripts vs Make vs Just

Feature mise tasks npm scripts Make Just
Dependencies between tasks Yes No Yes No
Parallel execution Yes With concurrently Yes No
Language-agnostic Yes Node only Yes Yes
Argument passing Yes Awkward Yes Yes
Environment per task Yes No No Yes
Built into your version manager Yes No No No

The advantage of mise tasks is consolidation. Your version management, environment variables, and task runner are all in one .mise.toml file.

Migrating from Other Tools

From asdf

mise reads .tool-versions files natively. Migration is often zero-effort:

# Your existing .tool-versions works as-is
# nodejs 22.5.0
# python 3.12.4

# Optionally convert to .mise.toml
mise settings set experimental true
mise config migrate

Most asdf plugins work with mise. The asdf: prefix lets you explicitly use asdf plugin backends:

[tools]
"asdf:hashicorp/terraform" = "1.7.0"

From nvm

# Remove nvm from your shell config
# Replace .nvmrc with mise config

# If you have .nvmrc with "22"
echo 'node = "22"' > .mise.toml

# mise also reads .nvmrc files if you prefer not to migrate

From pyenv

# Remove pyenv from your shell config
# Replace .python-version with mise config

# mise reads .python-version files
# Or set in .mise.toml:
# [tools]
# python = "3.12"

From rbenv

Same pattern: mise reads .ruby-version files. Remove rbenv from your shell config, install mise, and your existing version files work without changes.

Team Workflows

Pinning Versions for Consistency

Commit your .mise.toml to version control. When a team member clones the repo:

# Automatically installs all required tools
mise install

# Everything matches the committed config
mise ls

This eliminates "works on my machine" problems caused by version mismatches.

Trust Model

mise requires explicit trust before loading project configs that set environment variables or run hooks:

# Trust a project's config
mise trust

# Trust a specific file
mise trust .mise.toml

# Revoke trust
mise trust --untrust

This prevents a cloned repo from silently running commands or setting variables. You must explicitly opt in for each project.

Performance

mise is written in Rust and noticeably faster than asdf (written in shell scripts):

Operation mise asdf
node --version (shim overhead) ~5ms ~200ms
install node@22 Same (downloads same binary) Same
Shell startup (activation) ~10ms ~50-100ms
ls (list installed) Instant ~500ms

The shim overhead matters most. If asdf adds 200ms to every command invocation, that compounds across a development session. mise's overhead is imperceptible.

Advanced Configuration

Hooks

Run commands automatically when entering or leaving a project directory:

[hooks]
enter = "echo 'Entering project: {{config_root}}'"
cd = "mise install --quiet"

Settings

Global settings in ~/.config/mise/config.toml:

[settings]
# Always install missing tools automatically
auto_install = true

# Use symlinks instead of shims (faster, but less compatible)
shims_dir = "~/.local/share/mise/shims"

# Number of parallel jobs for installs
jobs = 4

# Quiet output
quiet = false

Integration with CI

# GitHub Actions
- name: Install mise
  uses: jdx/mise-action@v2

- name: Install tools
  run: mise install

- name: Run tests
  run: mise run test

This ensures your CI environment matches your development environment exactly.

When Not to Use mise

mise is not the right tool if you only use a single runtime and have no plans to add more. If you only write Python and already have a working pyenv setup, the migration offers minimal benefit.

It is also not a container-based dev environment tool. If you want fully reproducible environments with system-level dependencies, look at Dev Containers or Nix instead. mise manages runtimes and tools, not operating system packages.

For everyone else -- anyone managing two or more runtimes, wanting per-project environment variables, or tired of maintaining separate version managers -- mise is the current best-in-class tool.