How to Use Mise: The Beginner's Guide (First Install, the Shell-Hook Rule, the Five-Command Loop, and the Trust Lesson)
CodexFuse Team · 2026-11-01T04:55:45.295Z · 14 min read
Mise is the polyglot tool version manager that replaces asdf, nvm, pyenv, and direnv with one binary. This beginner's guide covers the install, the shell-hook rule, the five-command loop, project vs global mise.toml, the trust lesson, [env], and [tasks].
Learning how to use Mise is the fastest way to stop the "works on my machine" conversation with yourself. Mise is a polyglot tool version manager — one binary that installs and pins the exact versions of Node, Python, Go, Terraform, and hundreds of other tools per project, then switches between them automatically as you move between directories. This mise tutorial for beginners runs the complete first arc in one guide: the install, the shell-hook rule (nothing works until this runs), the five-command loop (mise use, mise install, mise ls, mise x, mise doctor), project-vs-global mise.toml, the trust lesson that stalls every first-timer, the [env] section, and [tasks]. If containers are new to you, our Docker beginner guide explains the other half of reproducible environments — this guide is the language-toolchain half.
What Is Mise?
Mise is a command-line tool version manager that replaces asdf, nvm, pyenv, and direnv with a single binary. Before tools like mise existed, a developer working on a Node 18 API, a Python 3.12 data script, and a Terraform 1.9 deployment needed three separate version managers, each with its own config file and shims — and a new machine setup meant remembering all of them. Mise collapses that stack: one mise.toml file per project declares every tool and version, and mise installs and activates the right set whenever you enter the directory.
The project lives as jdx/mise on GitHub, and as of this writing the latest release is v2026.10.25, published October 31, 2026 (the release notes headline: edit and remove tasks from the CLI, plus mise watch --once). Mise is mise for beginners the same way asdf was for the generation before it — except it ships as a single binary with no runtime of its own, and speaks asdf's config language, so you can migrate one project at a time.
How Do You Use Mise? The 10-Step First Arc
Here is the complete beginner path this guide builds, step by step:
- Install mise with the one-line installer (
curl -fsSL https://mise.run | sh). - Add the shell hook to your shell's startup file — the single rule that makes everything work.
- Restart your shell (or source the startup file) so the hook is live.
- Run
mise doctorto confirm the setup is healthy. - Enter your project directory and pin your first tool:
mise use node@24. - Trust the config when mise asks — this blocks the whole loop until you do.
- Check your tools with
mise ls --currentto see what is active. - Run something with
mise x -- node -vto prove the shim chain works. - Add environment variables with the
[env]section inmise.toml. - Define a task with the
[tasks]section and run it withmise run.
How Do You Install Mise?
You install mise with the official one-line installer, which puts the binary at ~/.local/bin/mise. Run curl -fsSL https://mise.run | sh in your terminal — this is the command documented on the official getting-started page and inside the installer script itself, and it is the same surface as of November 2026. Verify the install with ~/.local/bin/mise --version, which should report v2026.10.25 or newer.
Three other install paths cover everyone else. On Windows, winget install jdx.mise installs natively — but note the Windows caveat: mise runs natively only for tools and backends that publish Windows builds, because asdf plugins are shell scripts that need a Unix environment. If you used a package manager (Homebrew, apt, dnf), the activation commands below use plain mise instead of the full ~/.local/bin/mise path. Our Homebrew beginner guide covers the package-manager lane for macOS users.
What Is the Shell-Hook Rule in Mise?
The shell-hook rule is simple: no mise behavior works until your shell runs mise activate on every prompt — directory switching, version resolution, and task shims all depend on it. The hook is a small shell integration that fires when you cd, reads the nearest mise.toml, and updates your PATH. Install first and skip the hook, and every mise use below will seem to do nothing.
Add the hook to your shell's startup file, using the exact current syntax from the official docs:
# bash
echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc
# zsh
echo 'eval "$(~/.local/bin/mise activate zsh)"' >> "${ZDOTDIR:-$HOME}/.zshrc"
# fish
echo '~/.local/bin/mise activate fish | source' >> ~/.config/fish/config.fish
PowerShell users add (&mise activate pwsh) | Out-String | Invoke-Expression to $PROFILE. After adding the hook, restart the shell (or source the file) and run mise doctor — it checks the setup and will also surface a failed tool update. If doctor reports a problem, fix it before going further; the five-command loop assumes a healthy activation.
What Is the Five-Command Loop in Mise?
The five-command loop is the daily vocabulary of mise: mise use declares a tool, mise install installs what is declared, mise ls shows what is active, mise x runs one command in the mise environment, and mise doctor checks the setup. You will run the first four dozens of times a week; the fifth is your diagnostic when something looks wrong.
| Command | What it does | Writes config? |
|---|---|---|
mise use node@24 | Installs Node 24 and records the version request in the project mise.toml | Yes |
mise install | Installs every tool the config declares; edits nothing | No |
mise ls --current | Lists installed tools and which version is active here | No |
mise x -- node -v | Runs one command with mise's tools on the PATH (shims run this internally) | No |
mise doctor | Checks setup health (flags a bad hook, stale tools, failed updates) | No |
The distinction between mise use and mise install is the one beginners mix up: use is the writer (it changes the config and installs), install is the reader (it installs exactly what the config says). Run mise use node@24 in a fresh project directory and mise fetches Node 24 and writes [tools] node = "24" into mise.toml. Teammates then clone the repo and run only mise install — same file, same versions, zero arguing about who installed what.
Two discovery commands round out the loop. mise ls-remote node lists the versions mise can install, and mise config ls shows which config files are currently in effect — the answer to "why is this version active here?" The official docs illustrate versions with node@24 and a resolved example like 24.11.1, so treat those as the canonical shapes when you read examples elsewhere.
How Does Mise Manage Project vs Global Versions?
Mise separates versions into project config (mise.toml in your project directory) and global config (~/.config/mise/config.toml), and the rule is: project config always wins over global. The mise global default is the sane baseline everywhere — written with mise use -g node@24 — and any project can override it by declaring its own version. Load order runs from the filesystem root down to your current directory, plus the global file and the system file at /etc/mise; files closest to your current directory win.
Three filename details matter in real projects. The project file can be named mise.toml, .mise.toml, or live under .config/, .mise/, or mise/. Personal per-project overrides belong in mise.local.toml — keep that file out of git, because it is for your machine only. And mise use writes to the project's mise.toml — the one exception: run it with your home directory as the working directory and it writes the global config, a footgun worth knowing.
For environments like staging vs production, mise supports per-environment files: mise.production.toml is selected with -E production or the MISE_ENV=production variable. If you need byte-exact reproducibility across CI machines, mise lock writes a mise.lock file pinning the exact resolved versions — the counterpart to a package lockfile for your toolchain.
What Is the Trust Lesson in Mise?
The trust lesson is the #1 stall every beginner hits: mise refuses to execute config files it does not trust, and the entire loop above stops until you approve the directory. A plain config that only lists tool versions (a [tools] table with version strings) needs no trust. But the moment the config contains [env], hooks, templates, tool option tables, or [settings] — anything that can execute or leak secrets — mise demands explicit trust first.
The prompt looks like this: mise config files in <dir> are not trusted. Trust them? with Yes/No/All choices. This is the mise trusted config system: files written by mise use or mise set are auto-trusted, and running mise install, mise exec, mise run, task shorthands, mise watch, or daemon commands implicitly records trust for the non-global configs they load — so you usually approve once and forget it. Critically, shell activation never asks: if a config is untrusted, the hook silently skips it with the warning <path> is not trusted, run mise trust to enable it, and your cd just quietly does nothing.
Manage trust deliberately. mise trust --show lists trusted files, mise trust [path] approves one, mise trust --all approves everything, and mise trust --untrust / --ignore revoke or skip. Trust records live in mise's state directory, and on CI (where the CI environment variable is set) everything is treated as trusted so pipelines never block. The security point is real: trusting a config means trusting the person who committed it, because [env] can source arbitrary shell scripts.
How Do You Set Environment Variables with Mise?
You set per-project environment variables under the [env] section of mise.toml, and they activate whenever you enter the directory. Values can be strings or integers (true becomes "true", false unsets the variable), and the values apply to mise exec, tasks, shims, and activated shells alike. The CLI form is mise set KEY=val and mise unset KEY — both auto-trust the config, per the lesson above.
Three directives in the _ table handle the patterns real projects need:
[env]
NODE_ENV = "development"
API_URL = "http://localhost:3000"
_.file = ".env" # load dotenv/JSON/YAML/TOML into the environment
_.path = ["bin"] # add project bin/ to PATH
_.source = "scripts/env.sh" # source a bash script's exports
The _.path directive deserves a callout: a plain PATH key inside [env] is ignored — you must use _.path to modify the search path. Tasks get their own environment too: [tasks.print] env = {...} scopes variables to that one task. If you run dev servers on rented machines, our what is VPS hosting guide covers where [env]-driven projects typically live in production.
How Do You Run Tasks with Mise?
You define tasks in the [tasks] section of mise.toml and run them with mise run <name> — and mise auto-installs any missing tools the task needs before it executes. This is the piece that retires half your shell scripts and the Makefile guilt: the task, its environment, and the exact tool versions it needs all live in one versioned file.
[tasks.hello]
description = "Say hello from mise"
run = "echo hello from mise"
Run it with mise run hello. Executable scripts in a mise-tasks/ directory become file tasks — add #MISE description="..." at the top to document them — and tasks can declare dependencies with depends = ["format", "test"]. mise tasks ls lists everything available. For a beginner, the mental model is: [tools] pins the toolchain, [env] sets the environment, and [tasks] records the commands — the project becomes self-describing.
Mise vs asdf: Which Should a Beginner Choose?
If you are coming from asdf, choose mise as a drop-in upgrade and migrate one project at a time. Mise reads .tool-versions files and can run asdf plugins, so teammates can keep using asdf while you switch projects over. Convert a project with mise generate config --tool-versions .tool-versions (preview first with --dry-run), and keep mise-only request syntax like prefix: or backend names out of files asdf still reads.
The one hard rule of the migration: mise installs its own copies of every tool — it does not reuse asdf's install directories, and the two managers must not share one. This is stated explicitly in the official asdf comparison docs. Expect duplicated disk usage during the transition window, then uninstall asdf last (global defaults translate as mise use -g node@24). Held as the comparison member only — this guide does not bless a separate asdf guide.
Mise vs nvm: What Changes?
Mise vs nvm is the graduation story: nvm pins Node versions, mise pins every toolchain in one file. If your project is Node-only and you are comfortable with nvm, nothing is broken — our nvm beginner guide covers that lane. The day you add Python scripts, Terraform, or a Java backend to the same project, the nvm lane runs out of road, and mise is what you graduate to: one mise.toml replacing the version-manager stack, with the same cd-and-it-switches behavior nvm users already know.
The two are never rivals on this site — nvm owns the node-only lane, mise owns the polyglot lane. A practical migration: keep your .nvmrc until mise's config carries the same Node version, then delete it and let mise.toml be the single source of truth. Your shell hook and muscle memory (node -v just works) stay the same either way.
Why Is Mise Not Working After cd?
When mise does nothing after a directory change, the cause is almost always one of three things: the shell hook is missing, the config is untrusted, or you are in the wrong directory. Run mise doctor first — it flags a dead hook. Then run mise config ls to see which config files mise actually loads in the current directory; if your mise.toml is missing from that list, you are one directory too high. If it appears but its [env] and [tasks] are inert, check the trust warning the hook prints on cd and run mise trust.
The subtler failures: mise use written from your home directory edited the global config instead of the project file; _.path vs a plain PATH key; a mise.local.toml overriding what you expect from the committed file. The five-command loop plus mise config ls diagnoses all of them in under a minute.
Sources
- Mise official getting-started guide (install command, shell-hook syntax, command reference) — https://mise.jdx.dev/getting-started.html
- Mise installer script (install path
~/.local/bin/mise) — https://mise.run - Mise glossary (config files, precedence, per-environment files, resolved-version examples) — https://mise.jdx.dev/glossary.html
- Mise security docs: configuration trust (trust triggers, auto-trust, prompt text, CI behavior) — https://mise.jdx.dev/security.html
- Mise environments docs (
[env]syntax,_.file/_.path/_.source,mise set) — https://mise.jdx.dev/environments/ - Mise tasks docs (
[tasks]syntax, file tasks,depends,mise run) — https://mise.jdx.dev/tasks/ - Mise vs asdf comparison (migration path, no shared install dirs, Windows caveat) — https://mise.jdx.dev/dev-tools/comparison-to-asdf.html
- Mise GitHub releases API (v2026.10.25, published 2026-10-31) — https://api.github.com/repos/jdx/mise/releases/latest
Frequently Asked Questions
Is mise a replacement for asdf, nvm, and pyenv?
Yes — that is its design brief. One mise binary replaces asdf's plugin system, nvm's Node management, pyenv's Python management, and direnv's environment switching, with a single mise.toml per project. The official docs frame the asdf migration as gradual: mise reads .tool-versions and runs asdf plugins, so you move one project at a time.
Does mise work on Windows?
Mise runs natively on Windows for tools and backends that publish Windows builds — install with winget install jdx.mise. The caveat is asdf plugins: they are shell scripts that need a Unix environment, and mise does not make them work natively on Windows. If your toolchain depends on an asdf plugin with no native backend, stay on WSL.
How is mise different from Homebrew?
Homebrew installs packages system-wide; mise pins versions per project. Brew gives you one Node for the whole machine, mise gives each project its own declared Node, Python, and Terraform that activate on cd. They coexist fine — many developers install mise itself via Homebrew, then let mise own the project toolchains.
Do I need mise if I use Docker?
Not for production parity, but it helps in development. Docker fixes the runtime; mise fixes the host toolchain you develop with — the editor plugins, linters, and scripts that run outside containers. Most mise users run both: mise for the local dev loop, containers for deployment.
How do I pin exact versions in mise?
Write the version in mise.toml (e.g. node = "24.11.1") or run mise lock to generate a mise.lock file with the exact resolved versions — the lockfile equivalent for your toolchain. Teammates running mise install get the exact resolved versions, which is what kills the "but it worked on my machine" class of bug.