docs/dev-tools/mise-lock.md
mise.lock is a lockfile that pins exact versions and checksums of tools for reproducible environments. Lockfiles are not created automatically—you must run mise lock to generate them. Once a lockfile exists, mise will keep it updated as tools are installed or upgraded.
The lockfile serves similar purposes to package-lock.json in npm or Cargo.lock in Rust:
mise.tomlGITHUB_TOKEN in most casesLockfiles are controlled by the lockfile setting:
# Enable lockfiles globally
mise settings lockfile=true
# Or set in mise.toml
[settings]
lockfile = true
mise.lock file exists, running mise install or mise use updates it with the exact versions installedmise.lock exists, mise will prefer locked versions over version ranges in mise.tomlmise.lock is a TOML file with a platform-based format that organizes asset information by platform:
# Example mise.lock
[[tools.node]]
version = "20.11.0"
backend = "core:node"
[tools.node.platforms.linux-x64]
checksum = "sha256:a6c213b7a2c3b8b9c0aaf8d7f5b3a5c8d4e2f4a5b6c7d8e9f0a1b2c3d4e5f6a7"
size = 23456789
url = "https://nodejs.org/dist/v20.11.0/node-v20.11.0-linux-x64.tar.xz"
[[tools.python]]
version = "3.11.7"
backend = "core:python"
[tools.python.platforms.linux-x64]
checksum = "sha256:def456..."
size = 12345678
# Tool with backend-specific options
[[tools.ripgrep]]
version = "14.1.1"
backend = "aqua:BurntSushi/ripgrep"
options = { exe = "rg" }
[tools.ripgrep.platforms.linux-x64]
checksum = "sha256:4cf9f2741e6c465ffdb7c26f38056a59e2a2544b51f7cc128ef28337eeae4d8e"
size = 1234567
Each platform in a tool's [tools.name.platforms] section uses a key format like "os-arch" (e.g., "linux-x64", "macos-arm64") and can contain:
checksum (optional): SHA256 or Blake3 hash for integrity verificationsize (optional): File size in bytes for download validationurl (optional): Original download URL for reference or re-downloadingEach tool entry ([[tools.name]]) can contain:
version (required): The exact version of the toolbackend (optional): The backend used to install the tool (e.g., core:node, aqua:BurntSushi/ripgrep)options (optional): Backend-specific options that identify the artifact (e.g., {exe = "rg", matching = "musl"})platforms (optional): Platform-specific metadata (checksums, URLs, sizes)The platform key format is generally os-arch but can be customized by backends:
linux-x64, macos-arm64, windows-x64ubi may include additional tool-specific information in the platform keyWhen using environment-specific configuration files (e.g., mise.test.toml), each environment gets its own lockfile:
| Config file | Lockfile |
|---|---|
mise.toml | mise.lock |
mise.test.toml | mise.test.lock |
mise.staging.toml | mise.staging.lock |
mise.local.toml | mise.local.lock |
mise.test.local.toml | mise.test.local.lock |
For example, with MISE_ENV=test:
MISE_ENV=test mise lock # creates mise.lock AND mise.test.lock
Tools from mise.toml go to mise.lock, tools from mise.test.toml go to mise.test.lock.
Resolution: When MISE_ENV=test, mise reads mise.test.lock for tools defined in mise.test.toml and mise.lock for tools in mise.toml. Environment-specific lockfiles are strictly scoped to their corresponding config — they only contain tools defined in that config.
This design means CI environments that don't set MISE_ENV only depend on mise.lock, so dev tool version bumps in mise.dev.lock won't invalidate CI caches.
Both mise.lock and mise.<env>.lock files should be committed to version control. mise.local.lock and mise.<env>.local.lock should be gitignored alongside their corresponding config files.
Tools defined in mise.local.toml (which is typically gitignored) use a separate mise.local.lock file. This keeps local tool configurations separate from the committed lockfile.
# mise.local.toml tools go to mise.local.lock
mise use --path mise.local.toml node@22
# Regular mise.toml tools go to mise.lock
mise use --path mise.toml node@20
Use mise lock --local to update the local lockfile for all platforms:
mise lock --local # update mise.local.lock
mise lock --local node python # update specific tools in mise.local.lock
The locked setting enforces that all tools have pre-resolved URLs in the lockfile before installation. This prevents API calls to GitHub, aqua registry, etc., ensuring fully reproducible installations.
# Enable strict mode
mise settings locked=true
# Or via environment variable
MISE_LOCKED=1 mise install
::: warning
All mise settings are global in scope. Setting locked = true in a project's mise.toml applies to all tool resolution, including tools from your global ~/.config/mise/config.toml. If you see warnings about global tools missing from the lockfile, run mise lock -g to generate a global lockfile.
:::
When enabled, mise install will fail if a tool doesn't have a URL for the current platform in the lockfile. To fix this, first populate the lockfile with URLs:
mise lock # generate URLs for all platforms
mise lock --platform linux-x64,macos-arm64 # or specific platforms
This is useful for CI environments where you want to guarantee reproducible builds without any external API dependencies.
# Generate the lockfile
mise lock
# Install tools using locked versions
mise install
# Install exact versions from lockfile
mise install
# Update tools and lockfile
mise upgrade
When you want to update tool versions:
# Update tool version in mise.toml
mise use node@26
# This will update both the installation and mise.lock
You can pin a specific version in the lockfile while keeping a fuzzy specifier in mise.toml:
# mise.toml has node = "latest" or node = "22"
mise upgrade [email protected] # installs 22.15.0 and updates mise.lock
mise lock [email protected] # updates mise.lock without reinstalling
If the version doesn't match the current config prefix, the config is updated automatically. For example, if mise.toml has node = "20" and you run mise upgrade [email protected], the config is bumped to node = "22" (preserving the same precision level) and the lockfile is set to 22.15.0.
The table below shows how each command interacts with mise.toml and mise.lock:
| Command | Installs | Updates mise.toml | Updates mise.lock |
|---|---|---|---|
mise use node@22 | Yes | Yes (sets node = "22") | Yes |
mise install | Yes | No | Yes |
mise install node | Yes | No | Yes (installs config version for node) |
mise install [email protected] | Yes | No | No (one-off install, not config-driven) |
mise upgrade | Yes | No | Yes |
mise upgrade node | Yes | No | Yes (upgrades node within its range) |
mise upgrade [email protected] | Yes | Only if version doesn't match prefix | Yes |
mise upgrade --bump | Yes | Yes (bumps prefix to match) | Yes |
mise lock | No | No | Yes (regenerates for all tools) |
mise lock [email protected] | No | Only if version doesn't match prefix | Yes |
Key points:
mise use is for changing which version you want in your config — it always writes to mise.tomlmise install installs what's in your config without changing it — mise install node installs the config's version of node and updates the lockfile, while mise install [email protected] is a one-off that doesn'tmise upgrade upgrades tools within their configured ranges and updates the lockfile — passing tool@version lets you target a specific versionmise lock regenerates lockfile entries without installing — passing tool@version lets you pin a specific versionBackend support for lockfile features varies:
aqua, http, github, gitlab
aqua, github, core:python (precompiled binaries), core:ruby (precompiled binaries), core:zig (install-time)vfox (tool plugins only)ubicore (some tools)asdf, npm, cargo, pipx# Always commit the lockfile
git add mise.lock
git commit -m "Update tool versions"
mise.toml with new version rangesmise install to update mise.lockmise install to get exact versions# Example GitHub Actions
- name: Install tools
run: |
mise install # Uses exact versions from mise.lock
- name: Cache lockfile
uses: actions/cache@v5
with:
key: mise-lock-${{ hashFiles('mise.lock') }}
If checksums become invalid or you need to regenerate them:
# Remove all tools and reinstall
mise uninstall --all
mise install
When merging branches with different lockfiles:
mise.lockmise install to verify everything works# In project's mise.toml
[settings]
lockfile = false
# Convert .tool-versions to mise.toml
mise config generate
# Enable lockfiles and generate the lockfile
mise settings lockfile=true
mise lock
mise install
# Set versions based on package.json
mise use node@$(jq -r '.engines.node' package.json)
When mise lock generates a lockfile, it records a provenance type (e.g., slsa, cosign, minisign, github-attestations) for each tool. For the current platform, mise downloads the artifact and performs full cryptographic verification at lock time -- ensuring the provenance entry in the lockfile is backed by actual verification, not just registry metadata. This applies to both the aqua and github backends. For cross-platform entries, provenance is detected from registry metadata without verification (since the artifact may not be runnable on the current machine).
By default, when mise install sees a lockfile with both a checksum and a provenance entry, it trusts the lockfile and skips re-verification. This avoids redundant API calls (e.g., GitHub attestation queries) which can cause rate limit issues in CI. Since the current platform's provenance was already verified during mise lock, this is safe.
For additional security, you can force provenance re-verification at install time on every install:
[settings]
locked_verify_provenance = true
Or via environment variable:
MISE_LOCKED_VERIFY_PROVENANCE=1 mise install
This is also automatically enabled in paranoid mode:
[settings]
paranoid = true
When enabled, every mise install will cryptographically verify provenance regardless of what the lockfile contains, ensuring the artifact was built by a trusted CI pipeline.
In addition to lockfiles, mise supports the minimum_release_age setting to limit supply chain risk by only installing versions that have been available for a minimum amount of time:
[settings]
minimum_release_age = "7d" # only resolve to versions released more than 7 days ago
This pairs well with lockfiles — use minimum_release_age to avoid picking up brand-new releases, and lockfiles to pin the exact versions you've vetted.
Some package-manager backends also forward this cutoff into transitive dependency resolution during
install. This includes npm: and pipx: tools.