Skip to content

Config hierarchy & precedence

Two teammates clone the same repo at the same commit and get two different node versions — no error, no warning, on either machine. The committed mise.toml is identical; what differs is everything mise layers on top of it before resolving a version. The course page's mental-model diagram (§2) names "Config loading" as one box — mise.toml · mise.local.toml · .tool-versions · global config — without saying which one wins when two of them disagree; this page slows down on exactly that: the precedence order, why it's a merge across a directory walk-up and not a single winner-takes-all file, and how to ask mise where a resolved value actually came from.

TL;DR — in 30 seconds:

  • Six sources contribute to a resolved value, from lowest to highest precedence: global config → project mise.toml.tool-versionsmise.local.tomlMISE_<TOOL>_VERSION env vars → CLI flags.
  • It's a merge across the directory walk-up, not "nearest file wins" — a key set only in a parent directory's mise.toml still applies to every subdirectory that doesn't override it.
  • An untrusted mise.toml contributes nothing at all — trust gates the whole precedence chain, so check trust state before reasoning about which file wins.

1. The sources, lowest precedence to highest

Six places can contribute to "what version of node, what env var, what task exists right now":

Source Scope Typically committed?
Global config~/.config/mise/config.toml Whole machine, every project without its own override No — per-machine
Project configmise.toml in the current directory and every parent directory, walking up One repo (or a subtree of it) Yes
.tool-versions Legacy asdf-format file, read for compatibility Yes (asdf-era repos)
Local overridesmise.local.toml Same directory as mise.toml, gitignored No — personal
Environment variablesMISE_<TOOL>_VERSION style Current shell session No — ephemeral
CLI flags / mise exec A single command invocation No — one-off

Read top to bottom as increasing precedence: a global default is the fallback everyone gets; a one-off CLI flag on a single command beats everything else, for that command only. .tool-versions sits below mise.toml — it exists so an asdf-era repo keeps working, not as a way to override a mise.toml that already covers the same tool. That's also where the two-teammates gap from the top of this page usually comes from: mise.local.toml and a MISE_<TOOL>_VERSION env var both outrank mise.toml in that table, and neither one shows up in git diff — a clean diff is no guarantee the resolved version matches.

2. It's a merge across the directory walk-up, not a single winner

The easy mistake: assuming mise finds the nearest mise.toml and stops there, ignoring everything above it. It doesn't. Starting from the current directory and walking up to the filesystem root, mise merges every mise.toml (and mise.local.toml) it finds:

  • For a key defined at more than one level (say, both the repo root and a subdirectory both pin node), the closer directory wins.
  • For a key defined only further up (a repo-root [env] var with no override in the subdirectory), it still applies — the subdirectory inherits it.
flowchart TD
    Root["Repo root mise.toml<br/>node = 20 · [env] FOO=bar"]
    Sub["Subdirectory mise.toml<br/>node = 18 (no [env] override)"]
    Root -->|closer directory wins for node| Eff
    Root -.->|inherited, not overridden| Eff
    Sub --> Eff["Effective config in subdirectory<br/>node = 18 · FOO=bar"]

This is the same layering course.md §8 describes for [env] ("a child mise.toml's [env] overrides its parent's") — it's not special to env vars, it's how every section ([tools], [env], [tasks], [vars]) resolves.

3. Trust gates the whole chain

Precedence only matters among files mise actually reads. An untrusted mise.toml contributes nothing — not "low priority," genuinely ignored, [tools]/[env]/[tasks]/[hooks] and all — until mise trust (course.md §10). So the practical order of operations is: trust first, then precedence decides which trusted file's value wins.

4. Diagnosing where a value came from

Three commands answer "why is this the version I'm getting?" at increasing levels of detail:

mise current                                  # resolved versions, no explanation of source
mise config ls                                # every config file mise loaded here, in precedence order
mise doctor                                   # config paths, trust state, activation health

mise config ls is the one to reach for when a version doesn't match what you expected: it lists the files mise actually merged for the current directory, in the order they were applied — the fastest way to confirm whether a stray mise.local.toml or a parent directory's mise.toml is the culprit.

flowchart BT
    G["Global config<br/>~/.config/mise/config.toml"] --> M
    T[".tool-versions<br/>(legacy, asdf-compat)"] --> M
    P["Project mise.toml<br/>(cwd + each parent dir, merged)"] --> M
    L["mise.local.toml<br/>(gitignored, per-machine)"] --> M["Resolved config<br/>tools · env · tasks · vars"]
    E["MISE_&lt;TOOL&gt;_VERSION<br/>env vars"] --> M
    C["CLI flags / mise exec<br/>(single invocation)"] --> M
    M --> R["Effective value<br/>(what mise current shows)"]

Precedence increases bottom-to-top of the diagram's inputs — global config is the widest net, a CLI flag is the narrowest and always wins for the command it's attached to.

Common gotchas

  • Trusting a clean git diff on mise.toml as proof the resolved version matches. Fix: it isn't — mise.local.toml and MISE_<TOOL>_VERSION env vars both outrank mise.toml and never show up in a diff; run mise config ls to see every source that actually contributed.
  • Assuming the nearest mise.toml wins and a parent directory's file is ignored. Fix: it's a merge across the whole directory walk-up, not a single winner — a key set only in a parent still applies to every subdirectory that doesn't override it.
  • Forgetting that an untrusted mise.toml contributes nothing at all. Fix: it isn't "low priority," it's genuinely ignored — every section, [tools]/[env]/[tasks]/[hooks] — until mise trust runs; check trust state before reasoning about precedence.
  • Guessing which file set a value instead of checking. Fix: mise config ls lists every config file mise merged for the current directory, in precedence order — always confirm with it rather than asserting from memory.
Check yourself — you clone a repo, git diff on mise.toml is clean, but mise current shows a different node version than a teammate's machine. What could explain this, and which command would confirm it?

A gitignored mise.local.toml override or a MISE_NODE_VERSION env var set in one shell — both outrank mise.toml and never enter git diff. mise config ls lists every file mise actually merged for the directory, in precedence order, and would surface the extra source.

Check yourself — a subdirectory has its own mise.toml that only sets [tasks]. Does it inherit the [tools] versions pinned in the repo-root mise.toml, or does defining its own file replace the parent entirely?

It inherits. mise merges every mise.toml found while walking up from the current directory to the root — a key defined only in a parent (like the root's [tools]) still applies wherever nothing closer overrides it; defining a file doesn't erase the parent's other keys.

You can defend this when you can name, for a given tool version or env var, which of the six sources set it; explain why a subdirectory's mise.toml doesn't erase its parent's other keys; and use mise config ls to confirm a precedence guess instead of asserting it from memory.

Go deeper: the complete configuration hierarchy — every source mise reads and the full merge behavior — is documented in the mise configuration docs; this page covers the 80/20 you need for the Do exercise.

5. How this connects

  • §1's ladder is the same "later, more specific layer wins" merge shape as two other modules' layering pages, which already name this page: Helmfile's environments & values precedence (module 06, Helmfile, §5) and Ansible's variable precedence ladder (module 08, Ansible, 01-inventory-and-vars.md §4) — three different tools, the same rule.
  • §2's directory walk-up merge isn't specific to [env] — this course's next page, Tasks (§5), shows the identical merge deciding which [tasks] a subdirectory sees, letting a repo-root ci task and a service-level test task coexist without either file knowing about the other.
  • Don't conflate this page's merge with this course's third page, Environments & backends (§1): that page's MISE_ENV profile switch changes which additional mise.<profile>.toml file gets merged in on top of the base config — a different axis from this page's directory walk-up, which decides which directory's files get merged — the two "environment" concepts answer different questions.