mise: The One Tool That Replaced Everything
One Rust binary replaces many config files. mise is the version manager, environment loader, task runner, and secrets manager I now use.
view .mdopen in claudeopen in chatgpt
I used to run many version managers on my machine. I used fnm for Node, uv for Python, direnv for environment variables, and corepack for pnpm. I also kept a Makefile for scripts that did not fit in package.json. That was five tools and five config files. Each one broke at a different time, for a different reason.
Then I found mise, a single Rust binary. It replaced all five tools.
What mise Actually Is
mise, formerly named rtx, is a polyglot version manager, environment loader, task runner, and secrets manager. It combines all of these into one tool written in Rust. It is not a new idea. It is what happens when someone looks at the full toolchain a developer maintains and puts it under one roof.
The idea is simple. mise works like asdf, but faster, with environment variables and tasks built in. If you have used asdf before, the mental model is the same. mise can even read asdf plugins directly. You can migrate by installing mise and removing asdf.
The Corepack Problem That Sold Me
I have shipped TypeScript packages for years. The package manager choice has always been messy: npm, pnpm, or yarn. Teams need a way to make sure everyone runs the same version.
Corepack was supposed to solve this problem. You run corepack enable, then declare the package manager in the packageManager field of package.json. Everyone must remember to do this. In practice, half the team forgot to enable Corepack. The other half kept old versions. The CI (continuous integration) pipeline then failed with unclear errors about mismatched package manager versions.
mise reads the packageManager field from package.json on its own. You do not need corepack enable or any global install. You clone the repo and run pnpm install. mise intercepts that command. It checks the version listed in package.json, downloads it if needed, and runs it. This happens without any extra step from you.
# Clone a new project
git clone https://github.com/team/project.git
cd project
# Run any command — mise handles the rest
pnpm install
# mise: pnpm@9.15.0 → installed, using
This feature sounds minor. It matters once you have debugged a CI failure caused by one developer running pnpm 8.15.0 locally while CI ran 8.12.0. A tool must just work. mise makes that true.
One Config to Rule Them All
The move from five config files to one file is why I still use mise. Everything lives in mise.toml:
[tools]
node = "22"
python = "3.12"
go = "1.22"
terraform = "1.9"
pnpm = "9.15.0"
[env]
_.file = ".env"
DATABASE_URL = "postgresql://localhost:5432/myapp"
NODE_ENV = "development"
[tasks.build]
run = "npm run build && docker build -t myapp ."
depends_on = ["lint"]
[tasks.lint]
run = "npm run lint"
sources = ["src/**"]
This one file holds versions, environment variables, and tasks. You no longer need a Makefile, an .envrc file, or package.json scripts hidden behind npm lifecycle hooks. There is one file, one place, and one mental model.
The Secrets I Do Not Put on Disk
The .env file has always bothered me. It is a plaintext file full of API keys that sits in the repo root. Sometimes it gets committed by accident and then removed from history later. This is fine for personal projects. For team projects, it is a liability.
mise connects directly to 1Password and HashiCorp Vault:
[env]
STRIPE_API_KEY = "{{ exec(command='op read op://engineering/stripe/api_key') }}"
VAULT_TOKEN = "{{ exec(command='vault kv get -field=token secret/myapp') }}"
mise pulls these values from the vault at shell startup. They are never written to disk and never copied between machines. When a new developer joins the team, they get access to the 1Password vault and install mise. Everything then loads automatically when they run cd into the project. The team no longer shares secrets in Slack or keeps a .env.example file that drifts out of date.
The Task Runner I Did Not Know I Needed
I have always used Makefiles for cross-platform scripts, such as make lint, make test, and make deploy. This works, but the syntax is hard for newcomers to read. Every new developer on the project asks the same question: what does make build actually do?
mise tasks document themselves and track their own dependencies:
[tasks.setup]
run = "mise install && npm install"
description = "Set up the full dev environment"
[tasks.dev]
run = "npm run dev"
depends_on = ["setup"]
[tasks.deploy]
run = "npm run build && wrangler deploy"
depends_on = ["lint", "test"]
sources = ["src/**"]
Run mise tasks to see a list of every task with its description. Run mise run deploy and mise checks dependencies, confirms the sources have not changed, and runs the pipeline. You no longer need make --dry-run to figure out what a task does.
For monorepos, mise supports tasks defined per workspace. Each sub-package can define its own tasks, and the parent project runs them together. I now use this pattern instead of npm workspace scripts.
What I Stopped Using
Here is what mise replaced in my setup:
| Before | After |
|---|---|
fnm + .nvmrc |
[tools] node = "22" |
uv + .python-version |
[tools] python = "3.12" |
direnv + .envrc |
[env] in mise.toml |
corepack enable |
Built-in packageManager reading |
Makefile |
[tasks] in mise.toml |
.env files |
1Password/Vault integration |
asdf |
mise (100% plugin compatible) |
Seven tools became one. This was the easiest tooling change I have made in years. Because mise’s plugin system works with asdf, every .tool-versions file I already had worked right away.
The Speed Difference Matters More Than You Would Think
mise is written in Rust. This is not just a marketing point. It changes how you use the tool.
asdf relies on Bash shims. Each time you run node, the shim checks the .tool-versions file, finds the correct version, and runs it. This adds a measurable delay to every command. On a fast machine, the delay is about 50 to 100 milliseconds. On CI, this delay adds up across hundreds of shell calls.
mise removes this shim overhead for installed tools and runs them directly. You notice the difference in two places. Terminal startup no longer lags when you run cd into a project. CI pipelines waste fewer seconds resolving versions across many install steps.
mise uses CalVer releases, in the format YYYY.M.X, so the version number itself gives the release date. The tool ships often, so you can tell whether a release is from last month or last year.
What I Am Still Figuring Out
CI/CD adoption is the one area I have not fully solved. mise works perfectly on my local machine. In a Docker-based CI pipeline, you must either build mise into the image or install it at the start of every job. The install overhead is small, since mise is a single static binary. But the caching strategy differs from what I used before. For projects where CI speed matters most, I still need to test whether mise or a lean Dockerfile with fixed version numbers performs better.
Team migration is mostly painless, but it carries a social cost. Every tool change requires a pull request, a conversation, and a few people updating their local setup. The idea of replacing five tools with one sounds appealing. But sounding appealing and actually happening are two different things.
The Point
The best development tool is the one you stop thinking about. mise does not make me write code faster. No tool does that, despite what marketing often claims. Instead, mise removes the five minutes before I start writing code: the version check, the environment load, the moment I wonder whether I enabled Corepack, and the make setup command that failed because someone changed a dependency.
I judge tools by asking what happens in two years. Will mise still exist? Its development pace, its Rust foundation, its asdf compatibility, and the range of its features suggest that it will. Is the config sustainable? Yes: it is one file, one schema, with no external dependencies for the config itself.
If your workflow involves juggling version managers, environment loaders, and task runners, mise can bring them together in one tool. It is not a clever trick, and it does not win awards for originality. But my development environment has never been this boring. Boring is exactly what infrastructure must be.