I migrated from asdf to mise for managing runtime versions. Since mise has built-in environment variable management, I also removed direnv.
Why I Chose mise
asdf has been my go-to version manager for a long time, but I decided to switch after discovering mise, a faster alternative built in Rust. For a detailed comparison, see the official mise comparison page.
Comparing asdf and mise
| Aspect | asdf | mise |
|---|---|---|
| Implementation | Shell scripts | Rust |
| Command execution | Shims (~120ms overhead) | Direct PATH updates (~5–10ms) |
| Tool installation | 3-step process: add plugin → install → set | Single mise use command |
| Config files | .tool-versions | .tool-versions / mise.toml |
| Environment variables | Not supported | Built-in support |
| Task runner | Not supported | Built-in support |
Migration Steps
The recommended approach is to install mise first, verify your tools work, and then uninstall asdf. This order is also recommended by the mise FAQ, ensuring zero downtime during the migration.
1. Install mise
Following the official documentation, install mise via Homebrew:
brew install mise2. Configure your shell
Remove the asdf-related configuration (such as . $(brew --prefix asdf)/libexec/asdf.sh) from your .zshrc, then add mise activation:
echo 'eval "$(mise activate zsh)"' >> ~/.zshrcNote: Do not activate both asdf and mise simultaneously. Remove the asdf line from .zshrc before adding the mise line.
Apply the configuration:
source ~/.zshrc3. Install your tools
mise does not carry over tools installed by asdf, so you’ll need to reinstall them. If your project already has a .tool-versions file, simply run the following in that directory:
mise installThis will install all tools listed in .tool-versions at once.
4. Verify the setup
Confirm that your tools are running through mise:
mise doctor
node -v
which node # Should point to ~/.local/share/mise/installs/node/...5. Uninstall asdf
Once you’ve confirmed everything works with mise, uninstall asdf:
brew uninstall asdf --forceRemoving direnv
mise has built-in environment variable management, making direnv unnecessary. The mise documentation also officially discourages using direnv alongside mise.
Uninstall direnv
brew uninstall direnvRemove the direnv-related configuration (such as eval "$(direnv hook zsh)") from your .zshrc as well.
Managing environment variables with mise
Settings previously written in .envrc for direnv can be migrated to mise.toml in your project:
[env]
DATABASE_URL = "postgres://localhost/mydb"
NODE_ENV = "development"
# Load .env files
_.file = ".env"
# Add to PATH
_.path = "./bin"mise.toml is automatically applied when you enter the directory and removed when you leave — the same behavior as direnv.
Comparing direnv and mise environment management
| Aspect | direnv | mise |
|---|---|---|
| Config file | .envrc (shell script) | mise.toml (TOML) |
.env loading | dotenv .env | _.file = '.env' |
| PATH additions | PATH_add ./bin | _.path = './bin' |
| Python venv | layout python | _.python.venv = { path = ".venv", create = true } |
| Script execution | Written directly in .envrc | _.source = "./script.sh" |
Conclusion
Migrating from asdf to mise takes just a few commands with Homebrew. By setting up mise first and removing asdf afterward, you can migrate safely with zero downtime.
On top of that, mise’s built-in environment variable management makes direnv unnecessary, consolidating version management and environment configuration into a single tool.
That’s all from the Gemba, where we migrated from asdf and direnv to mise.