Most projects accumulate a small pile of tooling just to get a developer started: one manager for the Node version, another for Python, a `.env` file for configuration, and a scattering of `npm run` scripts and shell aliases for common tasks. Each has its own config file and its own quirks.

**mise** collapses that pile into one tool and one file. It manages language runtimes (the way nvm or pyenv do), runs your project's tasks (the way a Makefile or `npm run` does), and loads environment variables per directory (the way direnv does) — all from a single `.mise.toml`. This guide covers what mise does, how to set it up, and how to keep it aligned with your DeployHQ deployments.

## What is mise?

mise (pronounced "meez," from the French *mise en place*) is a fast, Rust-based developer environment manager. It began as a drop-in replacement for **asdf** — it reads the same `.tool-versions` files — and grew into three tools in one:

1. **Runtime version manager** — install and switch between versions of Node, Python, Ruby, Go, and hundreds of other tools, per project.
2. **Task runner** — define build, test, and deploy tasks in config and run them with `mise run`.
3. **Environment manager** — set environment variables (and load `.env` files) automatically when you enter a project directory.

If you've used asdf, mise is a faster, more capable successor that still understands your existing `.tool-versions`. If you've been stitching together nvm + pyenv + direnv + a Makefile, mise replaces all four.

## Installing mise

```bash
# macOS / Linux
curl https://mise.run | sh

# Homebrew
brew install mise
```

Then activate mise in your shell so it can manage versions and env automatically:

```bash
# ~/.bashrc
eval "$(mise activate bash)"

# ~/.zshrc
eval "$(mise activate zsh)"
```

Activation is what lets mise swap runtime versions and load environment variables the moment you `cd` into a project.

## Managing runtimes

Add tools to a project with `mise use`, which installs the version and records it in your config:

```bash
mise use node@20
mise use python@3.12
mise use go@1.22
```

This writes a `.mise.toml` at the project root:

```toml
[tools]
node = "20"
python = "3.12"
go = "1.22"
```

Anyone who clones the repo and runs `mise install` gets the exact same toolchain. Because mise also reads `.tool-versions`, teams migrating from asdf can adopt it without changing a single file.

```bash
mise install          # install everything listed in the config
mise ls               # show installed and active versions
mise use node@22      # bump a version — updates .mise.toml
```

## Running tasks

Instead of a separate Makefile or a wall of `package.json` scripts, mise lets you define tasks right in `.mise.toml`:

```toml
[tasks.build]
run = "npm run build"

[tasks.test]
run = "npm test"

[tasks.deploy]
depends = ["build", "test"]
run = "echo 'ready to deploy'"
```

Run them by name, and mise resolves dependencies for you:

```bash
mise run build
mise run deploy       # runs build and test first
```

Tasks inherit the runtime versions and environment from the same file, so a task never runs against the wrong Node version or a missing variable.

## Managing environment variables

mise can set environment variables per project and load values from a `.env` file — the job you'd otherwise hand to direnv:

```toml
[env]
NODE_ENV = "development"
DATABASE_URL = "postgres://localhost/myapp"
_.file = ".env"        # load additional values from .env
```

When you enter the directory, those variables are set automatically; when you leave, they're unset. No more manually `source`-ing files or leaking one project's config into another.

For secrets you don't want in plaintext, keep them out of `.mise.toml` and encrypt them instead — you can [encrypt your .env files with dotenvx](https://www.deployhq.com/guides/dotenvx) and load the decrypted values at runtime, so nothing sensitive lands in your committed config.

## A complete `.mise.toml`

Here's how the three pieces fit together in one file:

```toml
[tools]
node = "20"
python = "3.12"

[env]
NODE_ENV = "development"
_.file = ".env"

[tasks.build]
run = "npm install && npm run build"

[tasks.test]
depends = ["build"]
run = "npm test"
```

One file now defines the runtimes, the environment, and the commands for the whole project. A new developer runs `mise install` and is ready to work.

## Using mise with DeployHQ

mise gives your team a consistent local environment — but your build server needs the same runtime versions, and it's worth knowing exactly how that hand-off works.

DeployHQ's build pipeline **intentionally ignores** version-manager files, including `.tool-versions`, renaming them during a build so they can't override your project's configured version and restoring them afterward. This keeps builds predictable regardless of which dotfiles are in the repo — the same behavior described in [how DeployHQ's build pipeline handles version files](https://www.deployhq.com/support/build-pipelines/using-nvmrc-files). In other words, `.mise.toml` and `.tool-versions` drive your *local* environment; you set the *build* runtime explicitly in DeployHQ.

The clean way to keep them aligned:

1. **Set the runtime version** in your project's [build pipeline](https://www.deployhq.com/features/build-pipelines) configuration (or a `.deploybuild.yaml`), matching the versions in your `[tools]` block.
2. **Reuse your task definitions as build steps.** The commands inside your mise tasks — `npm install && npm run build`, `npm test` — are exactly what your build pipeline should run. Defining them once in `.mise.toml` means your local `mise run build` and your deployment build command stay identical, so what passes locally is what ships.

Then enable [automatic deployments](https://www.deployhq.com/features/automatic-deployments) so every push builds with the configured runtime and runs the same steps you run locally — no drift between your machine and production.

## When mise is the right choice

Reach for mise when:

- Your stack spans **more than one language** and you're tired of a separate manager per runtime.
- You want **tasks, versions, and environment** described in one committed file instead of three.
- You're **already on asdf** and want a faster tool that reads your existing `.tool-versions`.

If your project is Node-only, dedicated [Node.js version managers like fnm and Volta](https://www.deployhq.com/guides/node-version-managers) may be all you need — they're lighter and purpose-built for that single job. mise earns its keep the moment a second runtime, a task runner, or per-project env enters the picture.

Ready to ship with the same toolchain your team develops on? [Create a free DeployHQ account](https://www.deployhq.com/signup) and connect your repository in minutes.