Mise Complete
Configure and operate Mise for deterministic developer environments. Use when installing runtime/tool versions, defining reusable tasks, managing layered environment variables, migrating from asdf/nvm/pyenv, or debugging mise.toml behavior in CI and local shells. Keywords: mise, mise.toml, tool versions, tasks, env, asdf migration, setup automation, dev environment.
npx skills add github:pantheon-org/tekhne --skill package-mgmt--mise-completeWhen to Use
Use this skill when setup, version management, or task automation needs to be standardized with Mise.
When Not to Use
Do not use this skill when the repository intentionally uses another tool manager as the source of truth.
Core Principles
- Keep one canonical
mise.tomlper project scope. - Pin tool versions for reproducible builds.
- Make tasks deterministic and non-interactive by default.
- Keep secrets out of committed task definitions.
Deterministic Workflow
- Detect existing tool managers and migration constraints.
- Define or update
mise.tomltool versions. - Add or normalize tasks for common workflows.
- Configure environment variables by scope.
- Install configured tools and validate with
mise install && mise doctor. - If
mise installfails, runmise doctorto diagnose path or plugin issues, then re-runmise install.
Quick Commands
Install configured tools
mise install
Expected result: configured runtimes are installed for the current project.
Verify environment health after install
mise doctor
Expected result: no errors reported; any warnings indicate misconfigured plugins or PATH issues to resolve before proceeding.
Verify active tool versions match pinned values
mise ls
Expected result: each tool shows the pinned version from mise.toml as the active version.
Pin a tool version
mise use node@22
Expected result: mise.toml updated with pinned Node version.
Run a named task
mise run test
Expected result: task executes with the environment defined by Mise.
Inspect effective environment
mise env
Expected result: resolved environment variables and tool paths are printed.
Evaluate this skill quality
pantheon-skill-auditor evaluate mise-complete --json
Expected result: updated audit dimensions and grade.
mise.toml Example
A minimal mise.toml showing pinned tool versions, a task definition, and a scoped environment variable:
[tools]
node = "22.4.0"
python = "3.12.3"
terraform = "1.8.5"
[tasks.test]
description = "Run test suite"
run = "npm test"
[tasks.lint]
description = "Run linter"
run = "npm run lint"
[env]
NODE_ENV = "development"
- All tool versions are pinned to exact releases for reproducibility.
- Tasks are self-contained; no implicit shell state is assumed.
- Environment variables are scoped to this project and not committed with secrets.
Anti-Patterns
NEVER keep floating tool versions in team repositories
- WHY: Unpinned versions cause machine-to-machine drift.
- BAD:
node = "latest"for shared project tooling. - GOOD: Pin exact versions required by CI and teammates.
- Consequence: Builds pass on one machine and fail on another.
NEVER embed secrets in committed mise.toml
- WHY: Version control is not a secret store.
- BAD: Hardcode API tokens in environment blocks.
- GOOD: Load secrets from external secret management or local env files.
- Consequence: Secret leakage and mandatory credential rotation.
NEVER define tasks that rely on implicit shell state
- WHY: Hidden state makes task outcomes non-deterministic.
- BAD: Task assumes manual
cdor prior env exports. - GOOD: Task commands are self-contained with explicit paths.
- Consequence: Task behavior differs across terminals and CI.
NEVER mix multiple tool managers as active authority
- WHY: Overlapping managers conflict on PATH and versions.
- BAD: Keep both
nvmand Mise controlling Node in CI. - GOOD: Migrate ownership per tool and remove overlapping activation.
- Consequence: Unpredictable runtime resolution and flaky builds.
NEVER skip mise doctor after installing tools
- WHY:
mise installcan succeed while PATH or plugin problems remain, and these surface later as confusing build failures. - BAD: Run
mise install, see no error and move on without checking health. - GOOD: Run
mise install && mise doctorand resolve any warnings before proceeding. - Consequence: Misconfigured plugins or PATH issues are discovered mid-build instead of at setup.
NEVER leave a failed mise install without diagnosing it
- WHY: Re-running the same command repeats the same failure and hides the PATH or plugin cause.
- BAD: Retry
mise installseveral times, or loosen the pin until it passes. - GOOD: Run
mise doctorto diagnose the path or plugin issue, fix it, then re-runmise install. - Consequence: Pins get weakened to hide a local setup fault.
NEVER define interactive tasks that wait for user input
- WHY: Tasks must be deterministic and non-interactive by default so they run the same locally and in CI.
- BAD: A task that prompts for confirmation before running.
- GOOD: A task whose commands run to completion with no prompt.
- Consequence: Unattended CI jobs hang or fail.
NEVER hardcode machine-specific absolute paths in mise.toml
- WHY: Paths such as a home directory only exist on one machine, so the file stops being portable.
- BAD:
PROJECT_DIR = "/Users/john/projects/myapp". - GOOD:
PROJECT_DIR = "{{ config_root }}". - Consequence: Teammates and CI resolve the wrong or missing directory.
NEVER keep more than one canonical mise.toml defining the same tools for one scope
- WHY: Duplicate definitions drift apart and leave it unclear which pins apply.
- BAD: Copies of the same tool pins in a repository root and a sub-package, edited independently.
- GOOD: One canonical
mise.tomlper project scope, with shared settings in the global config. - Consequence: Different directories resolve different versions of the same tool.
References
references/tools-installation.mdreferences/tools-versions.mdreferences/tools-migration.mdreferences/tasks-definition.mdreferences/tasks-execution.mdreferences/env-definition.mdreferences/env-hierarchies.mdreferences/config-structure.mdreferences/config-management.mdreferences/config-anti-patterns.md
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Runtime validation via eval scenarios proving the skill actually changes agent behaviour. Requires evals/ directory, instructions.json, ≥3 scenarios, and ≥80% instruction coverage in summary.json.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
Expert-only knowledge the AI couldn't derive on its own — domain-specific patterns, production gotchas, decision frameworks, and non-obvious tool behaviour. High scores mean <5% redundancy with what the model already knows.
A clear philosophy plus numbered step-by-step workflows with explicit entry/exit points. Should answer: what is the core principle, how to execute it, and when (and when not) to apply it.
Explicit NEVER/WHY/consequence triplets with side-by-side bad/good examples. Generic warnings score low — concrete failure modes with real impact (security, correctness, performance) score high.
Proper frontmatter, single-task focus, activation keywords, and cross-harness portability. Skills must work across 40+ agentic harnesses without hardcoded paths or agent-specific references.
SKILL.md as a <100-line navigation hub pointing to focused references/ files. Content should be lazy-loaded on demand, not front-loaded. Includes actionable 'When to Use' conditions per reference.
Appropriate rigidity for the skill type: strict rules for critical/security skills, balanced steps for process skills, flexible options for tool skills. Mismatched calibration (too rigid or too loose) reduces score.
Rich activation keywords and trigger scenarios in the description so the skill fires when needed. Best descriptions read like an exhaustive list of the tasks and concepts that should invoke this skill.
Copy-paste-ready code, complete runnable examples, real file paths and commands. Pseudocode and abstract descriptions score low — concrete, executable content scores high.
No eval scenarios available.
Configuration Anti-Patterns
Common Mistakes
Committing Secrets
❌ NEVER do this:
# mise.toml (committed to git)
[env]
API_SECRET = "sk_live_abc123xyz"
DATABASE_PASSWORD = "super_secret_password"
JWT_SECRET = "my-secret-key"
AWS_SECRET_ACCESS_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
✅ Instead:
# mise.toml (committed)
[env]
_.file = [".env", ".env.secrets"]
# .env.secrets (git-ignored)
API_SECRET=sk_live_abc123xyz
DATABASE_PASSWORD=super_secret_password
JWT_SECRET=my-secret-key
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# .gitignore
.env.secrets
mise.local.toml
Hardcoding Paths
❌ Don’t:
[env]
PROJECT_DIR = "/Users/john/projects/myapp"
DATA_DIR = "/Users/john/projects/myapp/data"
LOG_FILE = "/Users/john/projects/myapp/logs/app.log"
✅ Do:
[env]
PROJECT_DIR = "{{ config_root }}"
DATA_DIR = "{{ config_root }}/data"
LOG_FILE = "{{ config_root }}/logs/app.log"
Duplicate Global Configuration
❌ Don’t repeat in every project:
# project-1/mise.toml
[env]
EDITOR = "code"
BROWSER = "firefox"
GIT_EDITOR = "code --wait"
# project-2/mise.toml
[env]
EDITOR = "code"
BROWSER = "firefox"
GIT_EDITOR = "code --wait"
✅ Use global config:
# ~/.config/mise/config.toml
[env]
EDITOR = "code"
BROWSER = "firefox"
GIT_EDITOR = "code --wait"
Mixing Version Managers
❌ Don’t mix tools:
# .bashrc
eval "$(mise activate bash)"
eval "$(nvm load)"
eval "$(pyenv init -)"
eval "$(rbenv init -)"
✅ Use Mise exclusively:
# .bashrc
eval "$(mise activate bash)"
# mise.toml
[tools]
node = "20"
python = "3.11"
ruby = "3.2"
Ignoring Exit Codes
❌ Don’t:
[tasks.ci]
run = "npm run lint; npm test; npm run build"
# If lint fails, test and build still run
✅ Do:
[tasks.ci]
run = [
"npm run lint",
"npm test",
"npm run build"
]
# Stops on first failure
Hardcoding Environment Names
❌ Don’t:
[tasks.deploy-dev]
run = "deploy.sh --env development"
[tasks.deploy-staging]
run = "deploy.sh --env staging"
[tasks.deploy-prod]
run = "deploy.sh --env production"
✅ Do:
[env]
ENVIRONMENT = "development"
[tasks.deploy]
run = "deploy.sh --env {{ env.ENVIRONMENT }}"
Task Logic Duplication
❌ Don’t:
[tasks.test-api]
run = "cd packages/api && npm test"
[tasks.test-web]
run = "cd packages/web && npm test"
✅ Do:
# Root mise.toml
[tasks.test-all]
run = '''
for pkg in packages/*; do
mise run -C "$pkg" test
done
'''
# packages/*/mise.toml
[tasks.test]
run = "npm test"
Over-Installing Tools
❌ Don’t install everything:
[tools]
node = "20"
python = "3.11"
ruby = "3.2"
go = "1.21"
rust = "stable"
java = "17"
php = "8.2"
dotnet = "8"
✅ Install only what you need:
[tools]
node = "20"
Missing Descriptions
❌ Don’t:
[tasks.x]
run = "./scripts/complex-task.sh"
✅ Do:
[tasks.deploy]
description = "Deploy application to production"
run = "./scripts/deploy.sh"
Unorganized Configuration
❌ Don’t:
[tasks.build]
run = "npm run build"
[env]
NODE_ENV = "development"
[tools]
node = "20"
✅ Do:
[tools]
node = "20"
[env]
NODE_ENV = "development"
[tasks.build]
run = "npm run build"
Configuration Smells
Warning Signs
- Secrets in mise.toml
- Hardcoded paths
- Too many tools
- Missing descriptions
- No .gitignore
- Duplicate configuration
- Complex inline tasks
- No validation
Quick Audit
# Check for secrets
grep -i "secret\|password\|key" mise.toml
# Check for hardcoded paths
grep "/Users\|/home" mise.toml
# Validate configuration
mise doctor
Recovery
Cleanup Checklist
- Remove secrets from git history
- Add .gitignore entries
- Create .env.example
- Move secrets to .env.secrets
- Replace hardcoded paths
- Add task descriptions
- Pin tool versions
- Run
mise doctor
Pre-commit Hook
#!/usr/bin/env bash
# .git/hooks/pre-commit
if git diff --cached mise.toml | grep -iE "secret|password|key.*="; then
echo "ERROR: Possible secret in mise.toml"
exit 1
fi
Configuration Best Practices
Project Setup
Initial Configuration
# mise.toml - Start simple and grow as needed
[tools]
node = "20"
[env]
NODE_ENV = "development"
[tasks.dev]
run = "npm run dev"
Documentation
# ==============================================================================
# Project: My Application
# Description: Web application with Node.js backend
# Maintained by: Engineering Team
# ==============================================================================
[tools]
# Node.js 20 LTS for long-term stability
node = "20"
[env]
# Development environment by default
# Override in mise.local.toml for personal settings
NODE_ENV = "development"
README Integration
# My Application
## Setup
1. Install Mise: `curl https://mise.run | sh`
2. Install tools: `mise install`
3. Trust config: `mise trust`
4. Start development: `mise run dev`
## Configuration
- `mise.toml` - Project configuration (committed)
- `mise.local.toml` - Personal overrides (git-ignored)
- `.env.local` - Local secrets (git-ignored)
Version Control
What to Commit
# Commit these files
mise.toml # Shared team configuration
.mise/tasks/* # Shared tasks
.env.example # Environment template
README.md # Setup documentation
What to Ignore
# .gitignore
mise.local.toml
.env.local
.env.secrets
*.backup
Example .env.example
# .env.example - Copy to .env.local and fill in values
# Database
DATABASE_URL=postgresql://localhost/myapp
DATABASE_PASSWORD=
# API Keys
API_SECRET=
JWT_SECRET=
# External Services
STRIPE_API_KEY=
SENDGRID_API_KEY=
Team Collaboration
Onboarding New Developers
# Setup script (scripts/setup.sh)
#!/usr/bin/env bash
set -e
echo "Setting up development environment..."
# Check Mise installation
if ! command -v mise &> /dev/null; then
echo "Installing Mise..."
curl https://mise.run | sh
fi
# Install tools
echo "Installing tools..."
mise install
# Setup environment
if [ ! -f .env.local ]; then
echo "Creating .env.local from template..."
cp .env.example .env.local
echo "Please edit .env.local with your settings"
fi
# Trust configuration
mise trust
echo "Setup complete! Run 'mise run dev' to start."
Communication
# Document breaking changes in mise.toml
# ==============================================================================
# BREAKING CHANGE (2024-02-11): Updated Node.js to v20
# Run: mise install
# ==============================================================================
[tools]
node = "20"
Configuration Organization
Small Projects
# Keep it simple - everything in one file
[tools]
node = "20"
[env]
NODE_ENV = "development"
[tasks.dev]
run = "npm run dev"
[tasks.test]
run = "npm test"
[tasks.build]
run = "npm run build"
Medium Projects
# Group by concern with clear sections
# ==============================================================================
# Tools
# ==============================================================================
[tools]
node = "20"
python = "3.11"
# ==============================================================================
# Environment
# ==============================================================================
[env]
NODE_ENV = "development"
_.path = ["./bin"]
_.file = [".env", ".env.local"]
# ==============================================================================
# Development Tasks
# ==============================================================================
[tasks.dev]
run = "npm run dev"
[tasks.test]
run = "npm test"
# ==============================================================================
# Deployment Tasks
# ==============================================================================
[tasks.deploy]
depends = ["test", "build"]
run = "./scripts/deploy.sh"
Large Projects (Monorepo)
project/
├── mise.toml # Root: shared tools and environment
├── .mise/
│ └── tasks/ # Shared complex tasks
│ ├── ci-full
│ ├── release
│ └── deploy-all
├── packages/
│ ├── api/
│ │ ├── mise.toml # API-specific configuration
│ │ └── .mise/tasks/ # API tasks
│ ├── web/
│ │ ├── mise.toml # Web-specific configuration
│ │ └── .mise/tasks/ # Web tasks
│ └── shared/
│ └── mise.toml # Shared library configuration
Maintenance
Regular Updates
# Update tools
mise upgrade
# Update plugins
mise plugins update
# Check for issues
mise doctor
# Audit configuration
mise config validate
Version Pinning
# Pin exact versions in production
[tools]
node = "20.10.0" # Not "20" or "latest"
python = "3.11.5"
# Use ranges in development
[tools]
node = "20" # Latest 20.x
python = "3.11" # Latest 3.11.x
Deprecation Management
# Document deprecations
[tools]
# DEPRECATED: Migrating from Node 18 to 20
# Remove Node 18 after 2024-03-01
node = "20"
# TODO: Upgrade Python to 3.12
python = "3.11"
Security Best Practices
Secret Management
# NEVER commit secrets in mise.toml
[env]
_.file = [".env", ".env.secrets"]
# .env.secrets (git-ignored)
API_SECRET=actual-secret-here
Trust System
# Only trust configurations you understand
mise trust
# Review before trusting
cat mise.toml
# List trusted configs
mise trust list
Sensitive Data Patterns
# Use template variables for non-sensitive parts
[env]
DB_HOST = "localhost"
DB_PORT = "5432"
DB_NAME = "myapp"
# DB_PASSWORD loaded from .env.secrets
DATABASE_URL = "postgresql://postgres:{{ env.DB_PASSWORD }}@{{ env.DB_HOST }}:{{ env.DB_PORT }}/{{ env.DB_NAME }}"
Performance Optimization
Tool Installation
# Only install needed tools
[tools]
node = "20" # Required
# python = "3.11" # Commented out if not needed
Task Optimization
# Parallel tasks for independent operations
[tasks.ci-fast]
depends = ["lint", "type-check", "test:unit"] # Run in parallel
# Sequential tasks for dependent operations
[tasks.deploy]
run = [
"mise run build",
"mise run test",
"./scripts/deploy.sh"
]
Environment Loading
# Load only necessary files
[env]
_.file = [".env"] # Not 10 different files
# Use computed values instead of duplicates
API_HOST = "localhost"
API_PORT = "3000"
API_URL = "http://{{ env.API_HOST }}:{{ env.API_PORT }}"
Testing Configuration
Configuration Tests
#!/usr/bin/env bash
# scripts/test-config.sh
set -e
echo "Testing Mise configuration..."
# Test tool installation
mise install
# Test environment loading
mise env > /dev/null
# Test task definitions
mise tasks ls > /dev/null
# Test specific tasks
mise run test --dry-run
echo "Configuration tests passed!"
CI/CD Integration
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Mise
run: curl https://mise.run | sh
- name: Install tools
run: mise install
- name: Run tests
run: mise run ci
Documentation Standards
Inline Documentation
# ==============================================================================
# Tools: Runtime versions for development and production
# ==============================================================================
[tools]
# Node.js 20 LTS - primary runtime
node = "20"
# Python 3.11 - for build scripts
python = "3.11"
# ==============================================================================
# Environment: Project configuration
# ==============================================================================
[env]
# Application environment (development, staging, production)
NODE_ENV = "development"
# API endpoint (override in mise.local.toml for local development)
API_URL = "https://api.example.com"
Change Log
# ==============================================================================
# CHANGELOG
# ==============================================================================
# 2024-02-11: Updated Node.js from 18 to 20
# 2024-02-01: Added Python for build scripts
# 2024-01-15: Initial Mise configuration
# ==============================================================================
Anti-Patterns to Avoid
❌ Configuration Smells
# Too many tools
[tools]
node = "20"
python = "3.11"
ruby = "3.2"
go = "1.21"
rust = "stable"
java = "17"
# Only install what you need!
# Hardcoded paths
[env]
DATA_DIR = "/Users/john/projects/myapp/data"
# Secrets in configuration
[env]
API_SECRET = "secret-key-123"
# Duplicate logic
[tasks.test-api]
run = "cd api && npm test"
[tasks.test-web]
run = "cd web && npm test"
✅ Better Approaches
# Only necessary tools
[tools]
node = "20"
# Template paths
[env]
DATA_DIR = "{{ config_root }}/data"
# Secrets in separate files
[env]
_.file = ".env.secrets"
# Reusable patterns
[tasks.test]
run = "npm test"
# Call from root: mise run -C api test
Checklist for Good Configuration
- Tools are necessary and versions are specified
- No secrets committed to version control
- Environment variables are documented
- Tasks have clear descriptions
- mise.local.toml is in .gitignore
- .env.example exists for required variables
- Configuration is validated (
mise doctor) - README includes setup instructions
- Breaking changes are documented
- Configuration is tested in CI/CD
Configuration Management
Configuration Commands
Viewing Configuration
# Show current configuration
mise config show
# Show as JSON
mise config show --json
# List all configuration files
mise config ls
# Show configuration from specific directory
mise config show -C /path/to/project
Validating Configuration
# Validate all configuration files
mise doctor
# Check specific config file
mise config validate mise.toml
# Debug configuration issues
mise config show --debug
Generating Configuration
# Generate mise.toml from existing version files
mise config generate
# Generate from .tool-versions
mise config generate .tool-versions > mise.toml
# Generate from .node-version, .python-version, etc.
mise config generate --all > mise.toml
Configuration Editing
Manual Editing
# Edit project config
$EDITOR mise.toml
# Edit global config
$EDITOR ~/.config/mise/config.toml
# Edit local overrides
$EDITOR mise.local.toml
Programmatic Updates
# Add a tool
echo 'node = "20"' >> mise.toml
# Update environment variable
mise set NODE_ENV=production
# Remove tool
mise unset tools.node
Configuration Templates
Starter Template
# mise.toml
[tools]
# Add your tools here
# node = "20"
# python = "3.11"
[env]
# Add your environment variables here
# NODE_ENV = "development"
[tasks.dev]
description = "Start development"
run = "echo 'Add your dev command here'"
[settings]
experimental = false
Full Template
# ==============================================================================
# Tools: Define runtime versions
# ==============================================================================
[tools]
node = "20"
python = "3.11"
# ==============================================================================
# Environment: Project environment variables
# ==============================================================================
[env]
NODE_ENV = "development"
PROJECT_ROOT = "{{ config_root }}"
# PATH additions
_.path = ["{{ config_root }}/bin"]
# Load environment files
_.file = [".env", ".env.local"]
# ==============================================================================
# Tasks: Runnable commands
# ==============================================================================
[tasks.dev]
description = "Start development server"
run = "npm run dev"
[tasks.build]
description = "Build for production"
run = "npm run build"
[tasks.test]
description = "Run tests"
run = "npm test"
# ==============================================================================
# Settings: Mise behavior configuration
# ==============================================================================
[settings]
experimental = false
verbose = false
Configuration Migration
From asdf
# Convert .tool-versions to mise.toml
mise config generate .tool-versions > mise.toml
# Or migrate directly
mise install # Mise reads .tool-versions automatically
Manual migration:
# .tool-versions
node 20.10.0
python 3.11.0
# mise.toml
[tools]
node = "20.10.0"
python = "3.11.0"
From direnv
# Old .envrc
export NODE_ENV=development
export API_URL=http://localhost:3000
PATH_add ./node_modules/.bin
# New mise.toml
[env]
NODE_ENV = "development"
API_URL = "http://localhost:3000"
_.path = ["./node_modules/.bin"]
From nvm/pyenv/rbenv
# .nvmrc
20.10.0
# .python-version
3.11.0
# .ruby-version
3.2.0
# mise.toml
[tools]
node = "20.10.0"
python = "3.11.0"
ruby = "3.2.0"
Configuration Backup
Backup Strategy
# Backup mise configuration
cp mise.toml mise.toml.backup
# Backup global config
cp ~/.config/mise/config.toml ~/.config/mise/config.toml.backup
# Backup entire mise directory
tar -czf mise-backup.tar.gz ~/.config/mise ~/.local/share/mise
Version Control
# .gitignore
mise.local.toml
.env.local
*.backup
# Commit mise.toml
git add mise.toml
git commit -m "Add mise configuration"
Configuration Sharing
Team Configuration
# mise.toml (shared with team)
[tools]
node = "20"
python = "3.11"
[env]
NODE_ENV = "development"
[tasks.dev]
run = "npm run dev"
Setup for new team members:
git clone repo
cd repo
mise install # Installs tools
mise trust # Trust the configuration
mise run dev # Start development
Personal Overrides
# mise.local.toml (personal, git-ignored)
[env]
DATABASE_URL = "postgresql://localhost/myapp_johndoe"
DEBUG = "true"
[settings]
verbose = true
Configuration Troubleshooting
Common Issues
Configuration not loading:
# Check which configs are being used
mise config ls
# Verify syntax
mise config validate
# Check environment
mise env
Tool versions not applying:
# Check tool installation
mise list
# Verify configuration
mise config show
# Reinstall tools
mise install
Environment variables not set:
# Check environment loading
mise env
# Verify activation
eval "$(mise activate bash)"
# Debug environment
mise env --debug
Debugging Commands
# Full system check
mise doctor
# Verbose output
mise --verbose config show
# Debug mode
mise --debug config show
# Trace execution
mise --trace config show
Configuration Security
Sensitive Data
# mise.toml (committed)
[env]
_.file = [".env", ".env.secrets"]
# .env.secrets (git-ignored)
API_SECRET=secret-key
DATABASE_PASSWORD=password
Trust System
# Trust project configuration
mise trust
# Trust specific config file
mise trust mise.toml
# List trusted configs
mise trust list
# Revoke trust
mise trust revoke
.gitignore Setup
# Mise local configuration
mise.local.toml
# Environment files
.env.local
.env.secrets
*.secret
# Backup files
*.backup
mise.toml.backup
Configuration Best Practices
- Version Control mise.toml: Share tool versions with team
- Ignore mise.local.toml: Keep personal settings private
- Document Variables: Comment configuration sections
- Validate Regularly: Run
mise doctorfrequently - Backup Before Changes: Keep backups of working configs
- Use Templates: Start with starter templates
- Separate Secrets: Never commit sensitive data
- Trust Carefully: Only trust configurations you understand
Configuration Organization
Small Projects
project/
├── mise.toml # All configuration
├── mise.local.toml # Local overrides (git-ignored)
└── .env.local # Secrets (git-ignored)
Medium Projects
project/
├── mise.toml # Tools and environment
├── .mise/
│ └── tasks/ # Complex tasks
│ ├── deploy
│ ├── test
│ └── build
├── mise.local.toml # Local overrides
└── .env.local # Secrets
Large Projects (Monorepo)
project/
├── mise.toml # Root configuration
├── .mise/
│ ├── config.toml # Additional config
│ └── tasks/ # Shared tasks
├── packages/
│ ├── api/
│ │ └── mise.toml # API-specific config
│ └── web/
│ └── mise.toml # Web-specific config
└── mise.local.toml # Root overrides
Configuration Anti-Patterns
❌ Don’t: Commit Secrets
[env]
API_SECRET = "secret-key"
✅ Do: Use .env.local
[env]
_.file = ".env.local"
❌ Don’t: Duplicate Configuration
# In every package's mise.toml
[tools]
node = "20"
python = "3.11"
✅ Do: Use Root Configuration
# Root mise.toml
[tools]
node = "20"
python = "3.11"
# Packages inherit from root
❌ Don’t: Hardcode Paths
[env]
DATA_DIR = "/Users/john/projects/myapp/data"
✅ Do: Use Template Variables
[env]
DATA_DIR = "{{ config_root }}/data"
❌ Don’t: Mix Concerns
[env]
NODE_ENV = "development"
[tools]
node = "20"
[env]
PORT = "3000"
✅ Do: Group Related Items
[tools]
node = "20"
[env]
NODE_ENV = "development"
PORT = "3000"
Configuration File Structure
mise.toml Overview
The mise.toml file is the main configuration file for Mise, using TOML format.
Basic Structure
# Tool versions
[tools]
node = "20"
python = "3.11"
rust = "stable"
# Environment variables
[env]
NODE_ENV = "development"
API_URL = "http://localhost:3000"
# Tasks
[tasks.build]
description = "Build the project"
run = "npm run build"
# Settings
[settings]
experimental = true
Configuration Sections
[tools]
Specifies tool versions to install and manage:
[tools]
node = "20.10.0"
python = "3.11"
terraform = "1.6"
kubectl = "latest"
Supported formats:
- Exact version:
"20.10.0" - Major.minor:
"20.10" - Major only:
"20" - Version prefix:
"prefix:20.10.0" - Latest:
"latest" - Path:
"path:./custom-node"
[env]
Defines environment variables:
[env]
NODE_ENV = "development"
DATABASE_URL = "postgresql://localhost/myapp"
_.path = ["./bin", "./node_modules/.bin"]
_.file = [".env", ".env.local"]
Special keys:
_.path: Adds directories to PATH_.file: Loads environment from files_.source: Sources shell scripts
[tasks.*]
Defines executable tasks:
[tasks.dev]
description = "Start development server"
run = "npm run dev"
[tasks.test]
description = "Run tests"
depends = ["lint"]
run = [
"npm run test:unit",
"npm run test:integration"
]
Task properties:
description: Human-readable descriptionrun: Command(s) to executedepends: Task dependenciesenv: Task-specific environmentdir: Working directorysources: Input filesoutputs: Generated filesalias: Alternative names
[settings]
Mise behavior configuration:
[settings]
experimental = true
verbose = false
jobs = 4
Configuration Files
Project Configuration
mise.toml (committed):
[tools]
node = "20"
[env]
NODE_ENV = "development"
[tasks.dev]
run = "npm run dev"
mise.local.toml (git-ignored):
[env]
DATABASE_URL = "postgresql://localhost/myapp_johndoe"
DEBUG = "true"
Global Configuration
~/.config/mise/config.toml:
[settings]
experimental = true
[env]
EDITOR = "code"
_.path = ["$HOME/.local/bin"]
[tools]
# Global tools available everywhere
System Configuration
/etc/mise/config.toml:
[env]
CORPORATE_PROXY = "http://proxy.company.com:8080"
File Locations
Search Order
Mise searches for configuration files in this order:
./mise.local.toml(current directory)./mise.toml(current directory)../.mise.toml(parent directory)~/.config/mise/config.toml(global)/etc/mise/config.toml(system)
Explicit Paths
# Use specific config file
mise --config /path/to/config.toml run build
# Use config from directory
mise -C /path/to/project run test
Tool Configuration
Version Specification
[tools]
# Exact version
node = "20.10.0"
# Version range
python = "3.11"
# Latest
rust = "latest"
# Version prefix
ruby = "ref:main"
# Path to local installation
go = "path:$HOME/custom-go"
# System installation
java = "system"
Tool-Specific Options
[tools]
node = { version = "20", install_args = "--with-intl=full" }
python = { version = "3.11", virtualenv = ".venv" }
Multiple Versions
[tools]
node = ["18", "20", "21"] # Install multiple versions
Environment Configuration
Variable Definition
[env]
# Simple values
NODE_ENV = "development"
PORT = "3000"
# Multi-line values
PRIVATE_KEY = """
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
"""
# Template variables
PROJECT_ROOT = "{{ config_root }}"
DATA_DIR = "{{ config_root }}/data"
PATH Management
[env]
_.path = [
"{{ config_root }}/bin",
"{{ config_root }}/node_modules/.bin",
"$HOME/.local/bin"
]
File Loading
[env]
_.file = [
".env",
".env.local",
".env.{{ env.NODE_ENV }}"
]
Script Sourcing
[env]
_.source = "scripts/env-setup.sh"
Task Configuration
Inline Tasks
[tasks.build]
description = "Build the project"
run = "npm run build"
[tasks.test]
description = "Run tests"
depends = ["build"]
run = [
"npm run lint",
"npm test"
]
Task Properties
[tasks.deploy]
description = "Deploy to production"
depends = ["test", "build"]
run = "./scripts/deploy.sh"
dir = "{{ config_root }}"
env = { ENVIRONMENT = "production" }
sources = ["src/**/*", "package.json"]
outputs = ["dist/**/*"]
alias = "ship"
File-Based Tasks
# Tasks can also be defined in .mise/tasks/
# mise.toml can reference them
[tasks]
# No inline definition needed if file exists
Settings Configuration
Common Settings
[settings]
# Enable experimental features
experimental = true
# Verbose output
verbose = false
# Number of parallel jobs
jobs = 4
# Disable telemetry
telemetry = false
# Automatic activation
activate_aggressive = true
# Legacy version file support
legacy_version_file = true
# Always keep downloads
always_keep_download = false
# Plugin autoupdate
plugin_autoupdate_last_check_duration = "7d"
Task Settings
[settings]
task_output = "prefix" # or "interleave"
raw = false
Configuration Validation
Syntax Check
# Validate configuration
mise doctor
# Show parsed configuration
mise config show
# List configuration files
mise config ls
Common Issues
Invalid TOML syntax:
[env]
KEY = value # Missing quotes
Correct:
[env]
KEY = "value"
Comments
# This is a comment
[tools]
node = "20" # Inline comment
# Multi-line comment block
# explaining the configuration
[env]
API_URL = "http://localhost:3000"
Best Practices
- Commit mise.toml: Share tool versions and tasks with team
- Ignore mise.local.toml: Personal overrides stay local
- Document Sections: Add comments explaining configuration
- Group Related Items: Organize by purpose
- Use Templates: Leverage
{{ config_root }}for paths - Validate Regularly: Run
mise doctorto check configuration
Example Complete Configuration
# Project: My Application
# Description: Web application with Node.js and PostgreSQL
# ==============================================================================
# Tool Versions
# ==============================================================================
[tools]
node = "20.10.0"
terraform = "1.6"
kubectl = "latest"
# ==============================================================================
# Environment Variables
# ==============================================================================
[env]
# Application
NODE_ENV = "development"
PORT = "3000"
LOG_LEVEL = "debug"
# Database
DATABASE_URL = "postgresql://localhost/myapp_dev"
# Paths
PROJECT_ROOT = "{{ config_root }}"
_.path = [
"{{ config_root }}/bin",
"{{ config_root }}/node_modules/.bin"
]
# Load additional environment files
_.file = [".env", ".env.local"]
# ==============================================================================
# Development Tasks
# ==============================================================================
[tasks.dev]
description = "Start development server"
run = "npm run dev"
[tasks.build]
description = "Build for production"
run = "npm run build"
[tasks.test]
description = "Run all tests"
depends = ["lint"]
run = [
"npm run test:unit",
"npm run test:integration"
]
[tasks.lint]
description = "Lint code"
run = "eslint ."
# ==============================================================================
# Deployment Tasks
# ==============================================================================
[tasks.deploy]
description = "Deploy to production"
depends = ["test", "build"]
run = "./scripts/deploy.sh"
env = { ENVIRONMENT = "production" }
# ==============================================================================
# Settings
# ==============================================================================
[settings]
experimental = true
verbose = false
jobs = 4
Anti-Patterns
❌ Don’t: Mix Configuration Concerns
[env]
NODE_ENV = "development"
[tools]
node = "20"
[env]
PORT = "3000"
✅ Do: Group Related Configuration
[tools]
node = "20"
[env]
NODE_ENV = "development"
PORT = "3000"
❌ Don’t: Hardcode Paths
[env]
DATA_DIR = "/Users/john/projects/myapp/data"
✅ Do: Use Template Variables
[env]
DATA_DIR = "{{ config_root }}/data"
❌ Don’t: Commit Secrets
[env]
API_SECRET = "super-secret-key"
✅ Do: Use .env.local
[env]
_.file = [".env", ".env.local"]
Environment Variable Definition
Overview
Mise manages environment variables through the [env] section in mise.toml, providing automatic loading when entering project directories.
Basic Definition
[env]
NODE_ENV = "development"
DEBUG = "true"
API_URL = "http://localhost:3000"
DATABASE_URL = "postgresql://localhost/myapp_dev"
Variable Types
String Values
[env]
APP_NAME = "My Application"
VERSION = "1.0.0"
Numeric Values
[env]
PORT = "3000"
MAX_CONNECTIONS = "100"
TIMEOUT_MS = "30000"
Boolean Values
[env]
ENABLE_CACHE = "true"
DEBUG_MODE = "false"
PRODUCTION = "false"
Multi-line Values
[env]
PRIVATE_KEY = """
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----
"""
Template Variables
Use template syntax to reference other variables and built-in values:
[env]
PROJECT_ROOT = "{{ config_root }}"
DATA_DIR = "{{ config_root }}/data"
LOG_FILE = "{{ config_root }}/logs/app.log"
Built-in Template Variables
{{ config_root }}: Directory containingmise.toml{{ cwd }}: Current working directory
Variable Interpolation
[env]
API_URL = "http://localhost:3000"
WEB_URL = "http://localhost:8080"
CALLBACK_URL = "{{ env.API_URL }}/callback"
PATH Management
Adding to PATH
[env]
_.path = [
"{{ config_root }}/bin",
"{{ config_root }}/scripts",
"{{ config_root }}/node_modules/.bin"
]
PATH Order
Paths are prepended in the order listed:
[env]
_.path = [
"./bin", # Highest priority
"./scripts",
"$HOME/.local/bin" # Lowest priority
]
Environment File Loading
Loading from .env Files
[env]
_.file = ".env"
Multiple Environment Files
[env]
_.file = [
".env",
".env.local",
".env.{{ env.NODE_ENV }}"
]
Files are loaded in order; later files override earlier ones.
Conditional File Loading
# .env.development
DATABASE_URL=postgresql://localhost/myapp_dev
DEBUG=true
# .env.production
DATABASE_URL=postgresql://prod-server/myapp
DEBUG=false
[env]
NODE_ENV = "development"
_.file = ".env.{{ env.NODE_ENV }}"
Tool-Specific Environments
Python Virtual Environment
[tools]
python = "3.11"
[env]
VIRTUAL_ENV = "{{ config_root }}/.venv"
_.path = ["{{ config_root }}/.venv/bin"]
Node.js Environment
[tools]
node = "20"
[env]
NODE_ENV = "development"
NODE_OPTIONS = "--max-old-space-size=4096"
_.path = ["{{ config_root }}/node_modules/.bin"]
Go Environment
[tools]
go = "1.21"
[env]
GOPATH = "{{ config_root }}/.go"
GOBIN = "{{ config_root }}/.go/bin"
_.path = ["{{ config_root }}/.go/bin"]
Rust Environment
[tools]
rust = "stable"
[env]
CARGO_HOME = "{{ config_root }}/.cargo"
RUSTUP_HOME = "{{ config_root }}/.rustup"
_.path = ["{{ config_root }}/.cargo/bin"]
Environment Scopes
Project-Level (mise.toml)
# mise.toml
[env]
PROJECT_NAME = "my-app"
API_URL = "http://localhost:3000"
Global (~/.config/mise/config.toml)
# ~/.config/mise/config.toml
[env]
EDITOR = "code"
BROWSER = "firefox"
Local Override (mise.local.toml)
# mise.local.toml (git-ignored)
[env]
DATABASE_URL = "postgresql://localhost/myapp_johndoe"
DEBUG = "true"
Priority: mise.local.toml > mise.toml > global config
Sensitive Data Handling
Separate Secret Files
# mise.toml (committed)
[env]
NODE_ENV = "development"
_.file = [".env", ".env.local"]
# .env (committed)
API_URL=http://localhost:3000
PORT=3000
# .env.local (git-ignored)
API_SECRET=super-secret-key
DATABASE_PASSWORD=secret123
.gitignore Configuration
.env.local
mise.local.toml
*.secret
Mise Environment Variables
Built-in Variables
Automatically set by Mise:
MISE_CONFIG_ROOT: Directory containing mise.tomlMISE_PROJECT_ROOT: Project root directoryMISE_DATA_DIR: Mise data directory (~/.local/share/mise)MISE_CACHE_DIR: Mise cache directoryMISE_INSTALL_PATH: Installation path for tools
Using Built-in Variables
[env]
APP_ROOT = "{{ env.MISE_PROJECT_ROOT }}"
CACHE_DIR = "{{ env.MISE_CACHE_DIR }}/my-app"
Common Patterns
Database Configuration
[env]
DB_HOST = "localhost"
DB_PORT = "5432"
DB_NAME = "myapp_dev"
DB_USER = "postgres"
DATABASE_URL = "postgresql://{{ env.DB_USER }}@{{ env.DB_HOST }}:{{ env.DB_PORT }}/{{ env.DB_NAME }}"
API Configuration
[env]
API_HOST = "localhost"
API_PORT = "3000"
API_PROTOCOL = "http"
API_URL = "{{ env.API_PROTOCOL }}://{{ env.API_HOST }}:{{ env.API_PORT }}"
Feature Flags
[env]
FEATURE_NEW_UI = "true"
FEATURE_BETA_API = "false"
FEATURE_ANALYTICS = "true"
Best Practices
- Separate Public and Private: Commit public config, ignore secrets
- Use Descriptive Names:
DATABASE_URLnotDB_URL - Document Variables: Add comments explaining purpose
- Use Templates: Leverage
{{ config_root }}for paths - Environment-Specific Files: Use
.env.development,.env.production - Version Control: Commit
.env.example, ignore.env.local
Anti-Patterns
❌ Don’t: Commit Secrets
[env]
API_SECRET = "my-secret-key-123" # DON'T DO THIS
✅ Do: Use .env.local
# mise.toml
[env]
_.file = [".env", ".env.local"]
# .env.local (git-ignored)
API_SECRET=my-secret-key-123
❌ Don’t: Hardcode Paths
[env]
DATA_DIR = "/Users/john/projects/myapp/data"
✅ Do: Use Template Variables
[env]
DATA_DIR = "{{ config_root }}/data"
❌ Don’t: Duplicate Global Config
# In every project's mise.toml
[env]
EDITOR = "code"
BROWSER = "firefox"
✅ Do: Use Global Config
# ~/.config/mise/config.toml
[env]
EDITOR = "code"
BROWSER = "firefox"
Environment Variable Hierarchies
Configuration Hierarchy
Mise uses a hierarchical configuration system where more specific configurations override more general ones.
Configuration Levels (Priority Order)
- Shell Environment (highest priority)
- Local Config (
mise.local.toml) - Project Config (
mise.toml) - Parent Directories (recursive search)
- Global Config (
~/.config/mise/config.toml) - System Config (
/etc/mise/config.toml)
Hierarchy Example
# /etc/mise/config.toml (system-wide)
[env]
EDITOR = "vi"
LOG_LEVEL = "info"
# ~/.config/mise/config.toml (user global)
[env]
EDITOR = "code" # Overrides system
GIT_EDITOR = "code --wait"
# ~/projects/myapp/mise.toml (project)
[env]
LOG_LEVEL = "debug" # Overrides global
API_URL = "https://api.example.com"
# ~/projects/myapp/mise.local.toml (local)
[env]
API_URL = "http://localhost:3000" # Overrides project
DEBUG = "true"
Result:
EDITOR = "code"(from global, overrides system)GIT_EDITOR = "code --wait"(from global)LOG_LEVEL = "debug"(from project, overrides global)API_URL = "http://localhost:3000"(from local, overrides project)DEBUG = "true"(from local)
Global Configuration
User-Level Global Config
Location: ~/.config/mise/config.toml
# Global settings for all projects
[env]
EDITOR = "code"
BROWSER = "firefox"
GIT_EDITOR = "code --wait"
LANG = "en_US.UTF-8"
# Global PATH additions
[env]
_.path = ["$HOME/.local/bin", "$HOME/bin"]
Use for:
- Personal editor/tool preferences
- User-specific paths
- Common environment variables across all projects
System-Level Global Config
Location: /etc/mise/config.toml
# System-wide settings (requires root)
[env]
CORPORATE_PROXY = "http://proxy.company.com:8080"
COMPANY_REGISTRY = "https://registry.company.com"
Use for:
- Organization-wide settings
- Corporate proxies
- Compliance requirements
Project Configuration
Main Project Config
Location: <project>/mise.toml
# Committed to version control
[tools]
node = "20"
python = "3.11"
[env]
NODE_ENV = "development"
API_URL = "https://api.example.com"
DATABASE_URL = "postgresql://localhost/myapp_dev"
[env]
_.file = [".env", ".env.local"]
Use for:
- Tool versions
- Project-specific variables
- Shared team configuration
Local Project Override
Location: <project>/mise.local.toml
# Git-ignored, developer-specific
[env]
DATABASE_URL = "postgresql://localhost/myapp_johndoe"
DEBUG = "true"
LOG_LEVEL = "trace"
Use for:
- Developer-specific overrides
- Local database connections
- Debug settings
- Sensitive data
Directory Traversal
Mise searches up the directory tree for configuration files:
/home/user/projects/myapp/services/api/
├── mise.toml (api service config)
└── src/
└── (working directory)
/home/user/projects/myapp/
└── mise.toml (root project config)
/home/user/
└── .config/mise/config.toml (global config)
Working in /home/user/projects/myapp/services/api/src/:
- Loads
/home/user/.config/mise/config.toml(global) - Loads
/home/user/projects/myapp/mise.toml(root project) - Loads
/home/user/projects/myapp/services/api/mise.toml(service) - Merges with later configs overriding earlier ones
Multi-Environment Setup
Environment-Specific Files
# mise.toml
[env]
NODE_ENV = "development"
_.file = ".env.{{ env.NODE_ENV }}"
# .env.development
API_URL=http://localhost:3000
DATABASE_URL=postgresql://localhost/myapp_dev
DEBUG=true
# .env.staging
API_URL=https://staging-api.example.com
DATABASE_URL=postgresql://staging-db/myapp
DEBUG=false
# .env.production
API_URL=https://api.example.com
DATABASE_URL=postgresql://prod-db/myapp
DEBUG=false
Switching Environments
# Development (default)
cd project
echo $API_URL # http://localhost:3000
# Staging
NODE_ENV=staging mise env
# or
echo 'NODE_ENV = "staging"' > mise.local.toml
# Production
NODE_ENV=production mise exec -- node app.js
Workspace Configuration
Monorepo Structure
myapp/
├── mise.toml # Root config
├── packages/
│ ├── api/
│ │ └── mise.toml # API-specific config
│ ├── web/
│ │ └── mise.toml # Web-specific config
│ └── shared/
│ └── mise.toml # Shared config
Root Configuration
# myapp/mise.toml
[tools]
node = "20"
[env]
PROJECT_ROOT = "{{ config_root }}"
NODE_ENV = "development"
Package-Specific Configuration
# myapp/packages/api/mise.toml
[env]
API_PORT = "3000"
DATABASE_URL = "postgresql://localhost/myapp_api"
LOG_LEVEL = "debug"
# Inherits NODE_ENV and PROJECT_ROOT from root
# myapp/packages/web/mise.toml
[env]
WEB_PORT = "8080"
API_URL = "http://localhost:3000"
# Inherits NODE_ENV and PROJECT_ROOT from root
Sensitive Data Handling
Separating Public and Private Configuration
# mise.toml (committed)
[tools]
node = "20"
[env]
NODE_ENV = "development"
API_URL = "http://localhost:3000"
# Load secret files
[env]
_.file = [".env", ".env.local", ".env.secrets"]
# .env (committed)
NODE_ENV=development
PORT=3000
# .env.local (git-ignored)
DATABASE_URL=postgresql://localhost/myapp_johndoe
# .env.secrets (git-ignored)
API_SECRET=super-secret-key
JWT_SECRET=another-secret
.gitignore Setup
# Ignore local overrides
mise.local.toml
.env.local
.env.secrets
*.secret
Environment Variable Templates
# mise.toml (committed)
[env]
DATABASE_HOST = "localhost"
DATABASE_PORT = "5432"
DATABASE_NAME = "myapp_dev"
# DATABASE_PASSWORD loaded from .env.secrets
# .env.secrets (git-ignored)
DATABASE_PASSWORD=secret123
Conditional Configuration
Platform-Specific Settings
[env]
# Use different paths based on OS
JAVA_HOME = "{{ if eq .os 'darwin' }}/Library/Java/Home{{ else }}/usr/lib/jvm/default{{ end }}"
Tool-Based Conditional Loading
[tools]
python = "3.11"
[env]
# Only set if Python is installed
PYTHON_PATH = "{{ config_root }}/.venv"
_.path = ["{{ config_root }}/.venv/bin"]
Configuration Inspection
View Merged Configuration
# Show final merged environment
mise env
# Show configuration sources
mise config ls
# Show configuration for specific directory
mise env -C /path/to/project
Debug Configuration
# Check which files are loaded
mise doctor
# Validate configuration syntax
mise config validate
# Show configuration as JSON
mise config show --json
Best Practices
- Global for Personal: User preferences in
~/.config/mise/config.toml - Project for Team: Shared settings in
mise.toml(committed) - Local for Individual: Personal overrides in
mise.local.toml(ignored) - Separate Secrets: Keep sensitive data in git-ignored files
- Document Hierarchy: Comment which settings override others
- Environment Files: Use
.env.exampleto document required variables
Common Patterns
Three-Tier Setup
# ~/.config/mise/config.toml (personal preferences)
[env]
EDITOR = "code"
# project/mise.toml (team settings)
[env]
NODE_ENV = "development"
API_URL = "https://api.example.com"
# project/mise.local.toml (your overrides)
[env]
API_URL = "http://localhost:3000"
DEBUG = "true"
Multi-Environment Pattern
# mise.toml
[env]
ENV = "development"
_.file = ".env.{{ env.ENV }}"
# Override environment
# mise.local.toml
[env]
ENV = "staging" # Now loads .env.staging
Anti-Patterns
❌ Don’t: Commit Secrets in Any Config
# mise.toml - DON'T DO THIS
[env]
API_SECRET = "secret-key-123"
✅ Do: Use Git-Ignored Files
# mise.toml
[env]
_.file = ".env.secrets"
# .env.secrets (git-ignored)
API_SECRET=secret-key-123
❌ Don’t: Duplicate Global Settings
# Don't repeat in every mise.toml
[env]
EDITOR = "code"
BROWSER = "firefox"
✅ Do: Use Global Config
# ~/.config/mise/config.toml
[env]
EDITOR = "code"
BROWSER = "firefox"
❌ Don’t: Hardcode Environment Names
[env]
DATABASE_URL = "postgresql://localhost/myapp_dev"
✅ Do: Use Environment-Specific Files
[env]
_.file = ".env.{{ env.NODE_ENV }}"
Environment Variable Loading
Activation Methods
Automatic Shell Integration
Enable automatic environment loading when entering directories:
# Add to ~/.bashrc, ~/.zshrc, etc.
eval "$(mise activate bash)" # or zsh, fish
When you cd into a directory with mise.toml, environment variables are automatically loaded.
Manual Activation
# Load environment in current shell
eval "$(mise env)"
# Load environment for specific shell
eval "$(mise env -s zsh)"
Running Commands with Environment
# Run command with mise environment
mise exec -- npm start
# Short form
mise x -- node app.js
# Run task (automatically includes environment)
mise run dev
Checking Current Environment
# Show all mise-managed environment variables
mise env
# Show specific variable
mise env | grep NODE_ENV
# Show environment for specific directory
mise env -C /path/to/project
Loading Order
Environment variables are loaded in this order (later overrides earlier):
- Global config (
~/.config/mise/config.toml) - Project config (
mise.toml) - Local config (
mise.local.toml) - Environment files (
.env,.env.local) - Shell environment (existing variables)
Example Loading Sequence
# ~/.config/mise/config.toml
[env]
EDITOR = "vim"
NODE_ENV = "development"
# project/mise.toml
[env]
NODE_ENV = "production" # Overrides global
API_URL = "https://api.example.com"
# project/mise.local.toml
[env]
API_URL = "http://localhost:3000" # Overrides project
DEBUG = "true"
Result:
EDITOR = "vim"(from global)NODE_ENV = "production"(from project, overrides global)API_URL = "http://localhost:3000"(from local, overrides project)DEBUG = "true"(from local)
Environment File Loading
Basic .env Loading
[env]
_.file = ".env"
# .env
NODE_ENV=development
API_URL=http://localhost:3000
DATABASE_URL=postgresql://localhost/myapp
Multiple File Loading
[env]
_.file = [
".env", # Base configuration
".env.local", # Local overrides
".env.{{ env.NODE_ENV }}" # Environment-specific
]
Load order: .env → .env.local → .env.development
Conditional Loading
[env]
NODE_ENV = "development"
# This will load .env.development
_.file = ".env.{{ env.NODE_ENV }}"
Optional File Loading
# Mise ignores missing files by default
# These won't error if files don't exist
[env]
_.file = [".env", ".env.local"]
Tool Environment Activation
Python Virtual Environments
[tools]
python = "3.11"
[env]
VIRTUAL_ENV = "{{ config_root }}/.venv"
_.path = ["{{ config_root }}/.venv/bin"]
# Automatically activates venv when entering directory
cd my-project
which python # Points to .venv/bin/python
Node.js Environment
[tools]
node = "20"
[env]
NODE_ENV = "development"
_.path = ["{{ config_root }}/node_modules/.bin"]
cd my-project
which eslint # Points to node_modules/.bin/eslint
Go Environment
[tools]
go = "1.21"
[env]
GOPATH = "{{ config_root }}/.go"
_.path = ["{{ config_root }}/.go/bin"]
Rust Environment
[tools]
rust = "stable"
[env]
CARGO_HOME = "{{ config_root }}/.cargo"
_.path = ["{{ config_root }}/.cargo/bin"]
Replacing direnv
Mise can fully replace direnv with more features:
direnv .envrc
# Old .envrc
export NODE_ENV=development
export API_URL=http://localhost:3000
PATH_add ./node_modules/.bin
Mise Equivalent
# mise.toml
[env]
NODE_ENV = "development"
API_URL = "http://localhost:3000"
_.path = ["./node_modules/.bin"]
Migration from direnv
- Remove
.envrcfiles - Create
mise.tomlwith equivalent configuration - Replace
direnv allowwith automatic mise activation - Use
mise trustif needed for project config
Debugging Environment Loading
Check Loaded Variables
# Show all mise environment variables
mise env
# Show as export statements
mise env -s bash
# Check specific variable
mise env | grep DATABASE_URL
Verify Loading Order
# Show which config files are loaded
mise config ls
# Show environment from specific config
mise env -C /path/to/project
Troubleshooting
# Check for syntax errors in mise.toml
mise doctor
# Validate environment configuration
mise env --json
# See which files would be loaded
mise config ls
Performance Optimization
Lazy Loading
Environment variables are loaded lazily - only when needed:
# Fast - doesn't load environment
cd project
# Loads environment only when needed
npm start # Environment loaded here
Caching
Mise caches environment calculations:
# First load - slower
cd project && mise env
# Subsequent loads - faster (cached)
cd project && mise env
Clear Cache
# Clear environment cache
mise cache clear
Shell-Specific Features
Bash/Zsh Completion
# Enable completion for environment variables
eval "$(mise activate bash)"
# Tab completion works for mise-managed vars
echo $NODE_<TAB>
Fish Shell
# ~/.config/fish/config.fish
mise activate fish | source
Nushell
# ~/.config/nushell/config.nu
mise activate nu
Common Patterns
Development vs Production
[env]
NODE_ENV = "development"
_.file = ".env.{{ env.NODE_ENV }}"
# Loads .env.development in dev
# Loads .env.production in prod
CI/CD Integration
# In CI pipeline
mise install # Install tools
mise exec -- npm test # Run with environment
Docker Integration
# Dockerfile
FROM node:20
WORKDIR /app
# Install mise
RUN curl https://mise.run | sh
ENV PATH="/root/.local/bin:$PATH"
# Copy config and install tools
COPY mise.toml .
RUN mise install
# Use mise to run application
CMD ["mise", "exec", "--", "npm", "start"]
Best Practices
- Use Shell Integration: Enable
mise activatefor automatic loading - Prefer mise exec: Use
mise execover manual env loading - Check Variables: Use
mise envto verify loaded variables - Cache Awareness: Clear cache if environment seems stale
- Trust Projects: Use
mise trustfor known projects
Anti-Patterns
❌ Don’t: Mix Environment Managers
# Don't mix direnv and mise
eval "$(direnv hook bash)"
eval "$(mise activate bash)"
✅ Do: Use One Manager
# Use mise exclusively
eval "$(mise activate bash)"
❌ Don’t: Override Mise Variables
# Don't override mise-managed variables
export NODE_ENV=production # Conflicts with mise
✅ Do: Configure in mise.toml
[env]
NODE_ENV = "production"
❌ Don’t: Source .env Manually
# Don't manually source files
source .env
✅ Do: Let Mise Handle It
[env]
_.file = ".env"
Environment Variable Patterns
Common Patterns
Multi-Environment Setup
Support multiple environments (development, staging, production):
# mise.toml
[env]
NODE_ENV = "development"
_.file = ".env.{{ env.NODE_ENV }}"
# .env.development
API_URL=http://localhost:3000
DATABASE_URL=postgresql://localhost/myapp_dev
LOG_LEVEL=debug
DEBUG=true
# .env.staging
API_URL=https://staging-api.example.com
DATABASE_URL=postgresql://staging-db/myapp
LOG_LEVEL=info
DEBUG=false
# .env.production
API_URL=https://api.example.com
DATABASE_URL=postgresql://prod-db/myapp
LOG_LEVEL=warn
DEBUG=false
ENABLE_CACHE=true
Usage:
# Development (default)
mise run dev
# Staging
NODE_ENV=staging mise run deploy
# Production
NODE_ENV=production mise run deploy
Database Configuration
Flexible database connection setup:
[env]
DB_HOST = "localhost"
DB_PORT = "5432"
DB_NAME = "myapp_dev"
DB_USER = "postgres"
DB_PASSWORD = "" # Set in .env.local
# Computed connection string
DATABASE_URL = "postgresql://{{ env.DB_USER }}:{{ env.DB_PASSWORD }}@{{ env.DB_HOST }}:{{ env.DB_PORT }}/{{ env.DB_NAME }}"
Override locally:
# mise.local.toml
[env]
DB_NAME = "myapp_johndoe"
DB_PASSWORD = "secret123"
Service Configuration
Configure multiple services with consistent patterns:
[env]
# API Service
API_HOST = "localhost"
API_PORT = "3000"
API_PROTOCOL = "http"
API_URL = "{{ env.API_PROTOCOL }}://{{ env.API_HOST }}:{{ env.API_PORT }}"
# Web Service
WEB_HOST = "localhost"
WEB_PORT = "8080"
WEB_PROTOCOL = "http"
WEB_URL = "{{ env.WEB_PROTOCOL }}://{{ env.WEB_HOST }}:{{ env.WEB_PORT }}"
# Database
DB_HOST = "localhost"
DB_PORT = "5432"
DATABASE_URL = "postgresql://{{ env.DB_HOST }}:{{ env.DB_PORT }}/myapp"
Feature Flags
Manage feature toggles through environment variables:
[env]
# Feature flags
FEATURE_NEW_UI = "true"
FEATURE_BETA_API = "false"
FEATURE_ANALYTICS = "true"
FEATURE_EXPERIMENTAL_SEARCH = "false"
# Feature-specific config
ANALYTICS_KEY = "UA-123456-1"
BETA_API_ENDPOINT = "https://beta-api.example.com"
Toggle features per environment:
# .env.development
FEATURE_NEW_UI=true
FEATURE_BETA_API=true # Enable in dev
# .env.production
FEATURE_NEW_UI=true
FEATURE_BETA_API=false # Disable in prod
Microservices Architecture
Configure multiple services in a monorepo:
# Root mise.toml
[env]
PROJECT_ROOT = "{{ config_root }}"
NODE_ENV = "development"
# Service registry
AUTH_SERVICE_URL = "http://localhost:3001"
USER_SERVICE_URL = "http://localhost:3002"
ORDER_SERVICE_URL = "http://localhost:3003"
PAYMENT_SERVICE_URL = "http://localhost:3004"
# Shared configuration
REDIS_URL = "redis://localhost:6379"
RABBITMQ_URL = "amqp://localhost:5672"
Per-service configuration:
# services/auth/mise.toml
[env]
SERVICE_NAME = "auth"
PORT = "3001"
DATABASE_URL = "postgresql://localhost/auth_db"
# services/user/mise.toml
[env]
SERVICE_NAME = "user"
PORT = "3002"
DATABASE_URL = "postgresql://localhost/user_db"
AUTH_SERVICE_URL = "{{ env.AUTH_SERVICE_URL }}"
CI/CD Integration
Environment setup for continuous integration:
[env]
# Detect CI environment
CI = "{{ env.CI | default 'false' }}"
CI_PIPELINE_ID = "{{ env.CI_PIPELINE_ID | default '' }}"
# CI-specific behavior
LOG_FORMAT = "{{ if eq .env.CI 'true' }}json{{ else }}pretty{{ end }}"
TEST_TIMEOUT = "{{ if eq .env.CI 'true' }}60000{{ else }}5000{{ end }}"
CI pipeline:
# .gitlab-ci.yml or .github/workflows/ci.yml
script:
- mise install
- mise exec -- npm test
- mise exec -- npm run build
Docker Integration
Environment configuration for containers:
[env]
# Detect Docker environment
IS_DOCKER = "{{ env.IS_DOCKER | default 'false' }}"
# Docker-specific paths
DATA_DIR = "{{ if eq .env.IS_DOCKER 'true' }}/data{{ else }}{{ config_root }}/data{{ end }}"
LOG_DIR = "{{ if eq .env.IS_DOCKER 'true' }}/var/log{{ else }}{{ config_root }}/logs{{ end }}"
# Container networking
API_HOST = "{{ if eq .env.IS_DOCKER 'true' }}0.0.0.0{{ else }}localhost{{ end }}"
Dockerfile:
FROM node:20
WORKDIR /app
RUN curl https://mise.run | sh
ENV PATH="/root/.local/bin:$PATH"
ENV IS_DOCKER=true
COPY mise.toml .
RUN mise install
CMD ["mise", "exec", "--", "npm", "start"]
Development Tools Integration
Configure editors and development tools:
[env]
# Editor settings
EDITOR = "code"
VISUAL = "code --wait"
# Git configuration
GIT_EDITOR = "code --wait"
GIT_MERGE_TOOL = "code"
# Language server settings
TSSERVER_LOG_FILE = "{{ config_root }}/logs/tsserver.log"
RUST_ANALYZER_LOG = "{{ config_root }}/logs/rust-analyzer.log"
# Debugging
DEBUG_PORT = "9229"
NODE_OPTIONS = "--inspect={{ env.DEBUG_PORT }}"
Secret Management
Secure sensitive data handling:
# mise.toml (committed)
[env]
# Public configuration
NODE_ENV = "development"
API_URL = "http://localhost:3000"
# Load secrets from separate files
[env]
_.file = [
".env", # Public config
".env.local", # Local overrides
".env.secrets" # Secrets (git-ignored)
]
# .env (committed)
NODE_ENV=development
PORT=3000
# .env.secrets (git-ignored)
API_SECRET=super-secret-key
JWT_SECRET=jwt-secret-123
DATABASE_PASSWORD=db-password
AWS_SECRET_ACCESS_KEY=aws-secret
# .env.example (committed)
# Copy to .env.secrets and fill in values
API_SECRET=
JWT_SECRET=
DATABASE_PASSWORD=
AWS_SECRET_ACCESS_KEY=
Logging Configuration
Flexible logging setup:
[env]
# Log level
LOG_LEVEL = "info"
# Log format
LOG_FORMAT = "json" # or "pretty"
# Log destinations
LOG_FILE = "{{ config_root }}/logs/app.log"
ERROR_LOG_FILE = "{{ config_root }}/logs/error.log"
# Service-specific logging
AUTH_LOG_LEVEL = "debug"
DB_LOG_LEVEL = "warn"
Environment-specific:
# .env.development
LOG_LEVEL=debug
LOG_FORMAT=pretty
# .env.production
LOG_LEVEL=info
LOG_FORMAT=json
Monorepo Workspace Configuration
Manage multiple packages with shared configuration:
# Root mise.toml
[env]
WORKSPACE_ROOT = "{{ config_root }}"
NODE_ENV = "development"
# Shared dependencies
POSTGRES_URL = "postgresql://localhost/myapp"
REDIS_URL = "redis://localhost:6379"
# Package paths
PACKAGE_API = "{{ config_root }}/packages/api"
PACKAGE_WEB = "{{ config_root }}/packages/web"
PACKAGE_SHARED = "{{ config_root }}/packages/shared"
Package-specific:
# packages/api/mise.toml
[env]
PACKAGE_NAME = "api"
API_PORT = "3000"
DATABASE_URL = "{{ env.POSTGRES_URL }}"
# packages/web/mise.toml
[env]
PACKAGE_NAME = "web"
WEB_PORT = "8080"
API_URL = "http://localhost:{{ env.API_PORT }}"
Testing Configuration
Environment setup for different test types:
[env]
# Test environment detection
NODE_ENV = "test"
# Test database
TEST_DATABASE_URL = "postgresql://localhost/myapp_test"
# Test Redis
TEST_REDIS_URL = "redis://localhost:6380"
# Test timeouts
UNIT_TEST_TIMEOUT = "5000"
INTEGRATION_TEST_TIMEOUT = "30000"
E2E_TEST_TIMEOUT = "60000"
# Test coverage
COVERAGE_THRESHOLD = "80"
Test-specific files:
# .env.test
NODE_ENV=test
DATABASE_URL=postgresql://localhost/myapp_test
LOG_LEVEL=error
Performance Tuning
Environment variables for performance:
[env]
# Node.js performance
NODE_OPTIONS = "--max-old-space-size=4096"
UV_THREADPOOL_SIZE = "8"
# Application performance
CACHE_TTL = "3600"
MAX_CONNECTIONS = "100"
REQUEST_TIMEOUT = "30000"
# Database tuning
DB_POOL_MIN = "2"
DB_POOL_MAX = "10"
DB_IDLE_TIMEOUT = "10000"
Best Practices
- Environment-Specific Files: Use
.env.<environment>pattern - Computed Values: Leverage template syntax for derived values
- Secret Separation: Keep secrets in git-ignored files
- Example Files: Provide
.env.examplefor documentation - Consistent Naming: Use prefixes (e.g.,
DB_,API_,FEATURE_) - Type Hints: Document expected values in comments
Anti-Patterns
❌ Don’t: Hardcode Environment-Specific Values
[env]
API_URL = "http://localhost:3000" # Breaks in production
✅ Do: Use Environment Files
[env]
_.file = ".env.{{ env.NODE_ENV }}"
❌ Don’t: Duplicate Configuration
[env]
API_PROTOCOL = "http"
API_HOST = "localhost"
API_PORT = "3000"
API_URL = "http://localhost:3000" # Duplication
✅ Do: Compute Derived Values
[env]
API_PROTOCOL = "http"
API_HOST = "localhost"
API_PORT = "3000"
API_URL = "{{ env.API_PROTOCOL }}://{{ env.API_HOST }}:{{ env.API_PORT }}"
❌ Don’t: Mix Concerns
[env]
DATABASE_URL = "postgresql://postgres:secret123@localhost/myapp"
✅ Do: Separate Configuration
[env]
DB_HOST = "localhost"
DB_USER = "postgres"
DB_PASSWORD = "" # Set in .env.local
DB_NAME = "myapp"
DATABASE_URL = "postgresql://{{ env.DB_USER }}:{{ env.DB_PASSWORD }}@{{ env.DB_HOST }}/{{ env.DB_NAME }}"
Task Definition
Overview
Mise tasks are defined in mise.toml under the [tasks] section or as standalone files in .mise/tasks/.
Inline Task Definition
[tasks.build]
description = "Build the project"
run = "npm run build"
[tasks.test]
description = "Run tests"
run = [
"npm run lint",
"npm run test:unit",
"npm run test:integration"
]
depends = ["build"]
File-Based Tasks
Create executable files in .mise/tasks/:
#!/usr/bin/env bash
# mise description="Deploy to production"
# mise depends=["build", "test"]
# mise alias="deploy:prod"
echo "Deploying..."
./scripts/deploy.sh --env production
Task Properties
description: Human-readable task descriptionrun: Command(s) to execute (string or array)depends: Tasks that must run firstalias: Alternative name for the taskenv: Environment variables specific to this taskdir: Working directory for task executionsources: Files that trigger task execution when changedoutputs: Files generated by the task
Multi-Command Tasks
[tasks.ci]
run = [
"mise run lint",
"mise run test",
"mise run build"
]
Task Arguments
Pass arguments to tasks:
mise run deploy -- --dry-run --env staging
Access in task:
[tasks.deploy]
run = "./deploy.sh $@"
Best Practices
- Descriptive Names: Use clear, action-oriented names
- Single Responsibility: Each task should do one thing well
- Composability: Build complex tasks from simple ones using
depends - Documentation: Always include descriptions
- Idempotency: Tasks should be safe to run multiple times
Common Patterns
Pre/Post Hooks
[tasks.deploy]
run = "./scripts/deploy.sh"
depends = ["pre-deploy"]
[tasks.pre-deploy]
run = [
"mise run test",
"mise run build"
]
[tasks.post-deploy]
run = "./scripts/notify-team.sh"
Conditional Execution
#!/usr/bin/env bash
# mise description="Deploy if tests pass"
if mise run test; then
mise run deploy
else
echo "Tests failed, deployment cancelled"
exit 1
fi
Multi-Stage Tasks
[tasks.release]
depends = ["test", "build", "version", "publish"]
[tasks.version]
run = "npm version patch"
[tasks.publish]
run = "npm publish"
Task Execution
Running Tasks
Basic Execution
# Run a single task
mise run build
# Run with short alias
mise r build
# List all available tasks
mise tasks ls
mise tasks
Task Dependencies
Dependencies run automatically before the main task:
[tasks.deploy]
run = "./deploy.sh"
depends = ["build", "test"]
# Running 'deploy' will automatically run 'build' and 'test' first
mise run deploy
Sequential vs Parallel Execution
Sequential (default for array of commands):
[tasks.ci]
run = [
"mise run lint", # Runs first
"mise run test", # Runs second
"mise run build" # Runs third
]
Parallel (for independent dependencies):
[tasks.ci]
depends = ["lint", "test"] # Run in parallel
run = "mise run build"
Task Arguments
# Pass arguments after --
mise run test -- --verbose --coverage
# Multiple arguments
mise run deploy -- --env production --region us-east-1
File Watching
Watch files and re-run tasks on changes:
# Watch and re-run on file changes
mise watch build
# Watch specific files
mise watch -g "src/**/*.ts" build
Configuration:
[tasks.dev]
run = "npm run dev"
sources = ["src/**/*.ts", "src/**/*.tsx"]
outputs = ["dist/**/*"]
Task Aliases
Define shortcuts for common tasks:
[tasks.test]
alias = "t"
[tasks.deploy-production]
alias = ["deploy:prod", "dp"]
mise run t # Runs test
mise run deploy:prod # Runs deploy-production
Environment Variables in Tasks
Task-Specific Environment
[tasks.test]
run = "pytest"
env = { PYTEST_ARGS = "--verbose", DEBUG = "true" }
Accessing Mise Variables
[tasks.deploy]
run = "echo Deploying from {{ config_root }}"
Built-in variables:
{{ config_root }}: Directory containing mise.toml{{ cwd }}: Current working directory
Monorepo Task Execution
Workspace Tasks
# Root mise.toml
[tasks.build-all]
run = "mise run -C packages/api build && mise run -C packages/web build"
# Or use a script
[tasks.build-all]
run = '''
for pkg in packages/*; do
mise run -C "$pkg" build
done
'''
Per-Package Tasks
# Run task in specific package
mise run -C packages/api test
# Run task in all packages
for pkg in packages/*; do
mise run -C "$pkg" lint
done
Task Output
Capturing Output
# Save output to file
mise run build > build.log 2>&1
# Pipe to other commands
mise run test | grep FAILED
Silent Execution
[tasks.check]
run = "curl -sf https://api.example.com/health"
Error Handling
Exit Codes
Tasks return the exit code of the last command:
[tasks.ci]
run = [
"mise run lint", # If this fails, task stops
"mise run test",
"mise run build"
]
Ignore Errors
#!/usr/bin/env bash
# mise description="Cleanup (ignore errors)"
rm -rf dist || true
rm -rf coverage || true
Conditional Execution
#!/usr/bin/env bash
set -e # Exit on error
mise run test && mise run deploy || echo "Deployment cancelled"
Best Practices
- Check Task Status: Use
mise tasks lsto verify task registration - Test Dependencies: Ensure dependent tasks run in correct order
- Use Exit Codes: Return proper exit codes for CI/CD integration
- Log Verbosity: Use environment variables to control logging
- Dry Run: Implement
--dry-runflags for destructive tasks - Timeouts: Set reasonable timeouts for long-running tasks
Common Patterns
Interactive vs CI Mode
#!/usr/bin/env bash
# mise description="Run tests"
if [ -t 0 ]; then
# Interactive mode
pytest --verbose
else
# CI mode
pytest --quiet --junit-xml=results.xml
fi
Retry Logic
#!/usr/bin/env bash
# mise description="Deploy with retry"
max_attempts=3
attempt=1
while [ $attempt -le $max_attempts ]; do
if ./deploy.sh; then
exit 0
fi
echo "Attempt $attempt failed, retrying..."
((attempt++))
sleep 5
done
exit 1
Progress Reporting
#!/usr/bin/env bash
# mise description="Multi-step build"
echo "Step 1/3: Linting..."
mise run lint
echo "Step 2/3: Testing..."
mise run test
echo "Step 3/3: Building..."
mise run build
echo "✓ Build complete"
Task Organization
Task Structure
Inline Tasks (mise.toml)
Use for simple, frequently-used tasks:
[tasks.dev]
description = "Start development server"
run = "npm run dev"
[tasks.build]
description = "Build for production"
run = "npm run build"
[tasks.test]
description = "Run all tests"
run = "npm test"
File-Based Tasks (.mise/tasks/)
Use for complex tasks with multiple steps:
.mise/
└── tasks/
├── deploy
├── release
├── db-migrate
└── ci-full
Example .mise/tasks/deploy:
#!/usr/bin/env bash
# mise description="Deploy application"
# mise depends=["build", "test"]
set -e
echo "Deploying to $ENVIRONMENT..."
./scripts/deploy.sh
Task Naming Conventions
Namespace by Purpose
# Development tasks
[tasks.dev]
[tasks."dev:api"]
[tasks."dev:web"]
# Testing tasks
[tasks.test]
[tasks."test:unit"]
[tasks."test:integration"]
[tasks."test:e2e"]
# Build tasks
[tasks.build]
[tasks."build:api"]
[tasks."build:web"]
# Deployment tasks
[tasks.deploy]
[tasks."deploy:staging"]
[tasks."deploy:production"]
Common Task Names
dev/start: Start development environmentbuild: Build for productiontest: Run testslint: Check code qualityformat: Format codeclean: Remove build artifactsinstall/setup: Install dependenciesdeploy: Deploy applicationrelease: Create a releaseci: Run CI pipeline locally
Task Composition
Dependency Graphs
[tasks.ci]
depends = ["lint", "test", "build"]
[tasks.lint]
run = "eslint ."
[tasks.test]
depends = ["lint"]
run = "npm test"
[tasks.build]
depends = ["test"]
run = "npm run build"
[tasks.deploy]
depends = ["ci"]
run = "./deploy.sh"
Execution order: lint → test → build → deploy
Reusable Task Components
[tasks.check-deps]
run = "npm outdated"
[tasks.check-security]
run = "npm audit"
[tasks.check-all]
depends = ["check-deps", "check-security"]
description = "Run all checks"
Monorepo Organization
Root-Level Tasks
# Root mise.toml
[tasks.install-all]
run = "npm install --workspaces"
[tasks.build-all]
run = '''
for pkg in packages/*; do
mise run -C "$pkg" build
done
'''
[tasks.test-all]
run = '''
for pkg in packages/*; do
mise run -C "$pkg" test
done
'''
Package-Level Tasks
# packages/api/mise.toml
[tasks.dev]
run = "tsx watch src/index.ts"
[tasks.test]
run = "vitest"
[tasks.build]
run = "tsup"
# packages/web/mise.toml
[tasks.dev]
run = "vite"
[tasks.test]
run = "vitest"
[tasks.build]
run = "vite build"
Cross-Package Dependencies
# packages/web/mise.toml
[tasks.dev]
depends = ["build-api"]
run = "vite"
[tasks.build-api]
run = "mise run -C ../api build"
Task Categories
Development Workflow
[tasks.setup]
description = "Initial project setup"
run = [
"npm install",
"cp .env.example .env",
"mise run db:migrate"
]
[tasks.dev]
description = "Start development"
depends = ["setup"]
run = "npm run dev"
[tasks.format]
description = "Format code"
run = "prettier --write ."
[tasks.lint]
description = "Lint code"
run = "eslint . --fix"
Testing Workflow
[tasks.test]
description = "Run all tests"
depends = ["test:unit", "test:integration"]
[tasks."test:unit"]
run = "vitest run"
[tasks."test:integration"]
run = "vitest run --config vitest.integration.config.ts"
[tasks."test:e2e"]
run = "playwright test"
[tasks."test:watch"]
run = "vitest watch"
[tasks."test:coverage"]
run = "vitest run --coverage"
Build & Release Workflow
[tasks.clean]
run = "rm -rf dist coverage .turbo"
[tasks.build]
depends = ["clean", "test"]
run = "npm run build"
[tasks.version]
run = "npm version patch"
[tasks.publish]
depends = ["build"]
run = "npm publish"
[tasks.release]
depends = ["test", "build", "version", "publish"]
description = "Full release workflow"
CI/CD Workflow
[tasks.ci]
description = "Run full CI pipeline"
depends = ["lint", "test", "build"]
[tasks."ci:quick"]
description = "Quick CI check"
depends = ["lint", "test:unit"]
[tasks.deploy]
depends = ["ci"]
run = "./scripts/deploy.sh"
Best Practices
- Group Related Tasks: Use namespaces (e.g.,
test:unit,test:integration) - Consistent Naming: Follow project conventions
- Clear Descriptions: Every task should have a description
- Logical Dependencies: Use
dependsto ensure prerequisites - Atomic Tasks: Each task should have a single, clear purpose
- Composable: Build complex workflows from simple tasks
File Organization
Recommended Structure
project/
├── mise.toml # Common tasks (dev, test, build)
├── .mise/
│ ├── config.toml # Mise configuration
│ └── tasks/
│ ├── deploy # Deployment tasks
│ ├── release # Release automation
│ ├── db-migrate # Database tasks
│ └── utils/
│ ├── check-deps # Utility scripts
│ └── cleanup
└── packages/
├── api/
│ └── mise.toml # API-specific tasks
└── web/
└── mise.toml # Web-specific tasks
Anti-Patterns
❌ Don’t: Duplicate Task Logic
[tasks.test-api]
run = "cd packages/api && npm test"
[tasks.test-web]
run = "cd packages/web && npm test"
✅ Do: Create Reusable Tasks
[tasks.test]
run = "npm test"
# In packages/api/mise.toml and packages/web/mise.toml
[tasks.test]
run = "npm test"
❌ Don’t: Hardcode Paths
[tasks.deploy]
run = "/Users/john/projects/myapp/deploy.sh"
✅ Do: Use Relative Paths
[tasks.deploy]
run = "./scripts/deploy.sh"
❌ Don’t: Ignore Exit Codes
[tasks.ci]
run = "npm run lint; npm test; npm run build"
✅ Do: Use Proper Exit Code Handling
[tasks.ci]
run = [
"npm run lint",
"npm test",
"npm run build"
]
Task Patterns
Common Task Patterns
Pre/Post Hooks
Execute tasks before and after main tasks:
[tasks.deploy]
depends = ["pre-deploy"]
run = "./scripts/deploy.sh"
[tasks.pre-deploy]
run = [
"mise run test",
"mise run build",
"mise run security-check"
]
[tasks.post-deploy]
run = [
"./scripts/notify-team.sh",
"./scripts/smoke-test.sh"
]
mise run pre-deploy && mise run deploy && mise run post-deploy
Conditional Execution
#!/usr/bin/env bash
# mise description="Conditional deployment"
if [ "$ENVIRONMENT" = "production" ]; then
echo "Deploying to production..."
./scripts/deploy-prod.sh
else
echo "Deploying to staging..."
./scripts/deploy-staging.sh
fi
Environment-Specific Tasks
[tasks.deploy-staging]
env = { ENVIRONMENT = "staging" }
run = "./scripts/deploy.sh"
[tasks.deploy-production]
env = { ENVIRONMENT = "production" }
run = "./scripts/deploy.sh"
Multi-Stage Builds
[tasks.build]
depends = ["build:clean", "build:compile", "build:optimize"]
[tasks."build:clean"]
run = "rm -rf dist"
[tasks."build:compile"]
run = "tsc"
[tasks."build:optimize"]
run = "esbuild dist/index.js --bundle --minify --outfile=dist/bundle.js"
Parallel Task Execution
[tasks.ci-fast]
depends = ["lint", "test:unit", "type-check"] # Run in parallel
run = "echo All checks passed"
[tasks.lint]
run = "eslint ."
[tasks."test:unit"]
run = "vitest run"
[tasks."type-check"]
run = "tsc --noEmit"
Testing Patterns
Test Pyramid
[tasks.test]
depends = ["test:unit", "test:integration", "test:e2e"]
[tasks."test:unit"]
description = "Fast unit tests"
run = "vitest run"
[tasks."test:integration"]
description = "Integration tests"
run = "vitest run --config vitest.integration.config.ts"
[tasks."test:e2e"]
description = "End-to-end tests"
depends = ["build"]
run = "playwright test"
Test with Coverage
[tasks."test:coverage"]
run = [
"vitest run --coverage",
"echo Coverage report: ./coverage/index.html"
]
Watch Mode
[tasks."test:watch"]
run = "vitest watch"
[tasks."dev:test"]
run = "vitest watch --ui"
Database Patterns
Migration Workflow
[tasks."db:migrate"]
description = "Run database migrations"
run = "knex migrate:latest"
[tasks."db:rollback"]
description = "Rollback last migration"
run = "knex migrate:rollback"
[tasks."db:reset"]
description = "Reset database"
run = [
"knex migrate:rollback --all",
"knex migrate:latest",
"knex seed:run"
]
[tasks."db:seed"]
description = "Seed database"
run = "knex seed:run"
Database Backup
#!/usr/bin/env bash
# mise description="Backup database"
# mise depends=["check-db-connection"]
timestamp=$(date +%Y%m%d_%H%M%S)
backup_file="backups/db_backup_${timestamp}.sql"
echo "Creating backup: $backup_file"
pg_dump $DATABASE_URL > "$backup_file"
echo "Backup complete"
Deployment Patterns
Blue-Green Deployment
[tasks."deploy:blue"]
env = { DEPLOYMENT_SLOT = "blue" }
run = "./scripts/deploy.sh"
[tasks."deploy:green"]
env = { DEPLOYMENT_SLOT = "green" }
run = "./scripts/deploy.sh"
[tasks."switch:to-blue"]
run = "./scripts/switch-traffic.sh blue"
[tasks."switch:to-green"]
run = "./scripts/switch-traffic.sh green"
Canary Deployment
#!/usr/bin/env bash
# mise description="Canary deployment"
# mise depends=["build", "test"]
echo "Deploying to 10% of traffic..."
./scripts/deploy.sh --canary 10
echo "Monitoring for 5 minutes..."
sleep 300
if ./scripts/check-health.sh; then
echo "Health check passed, deploying to 100%"
./scripts/deploy.sh --canary 100
else
echo "Health check failed, rolling back"
./scripts/rollback.sh
exit 1
fi
Zero-Downtime Deployment
[tasks.deploy-zero-downtime]
run = [
"./scripts/deploy-new-version.sh",
"./scripts/health-check.sh",
"./scripts/switch-traffic.sh",
"./scripts/shutdown-old-version.sh"
]
Release Patterns
Semantic Versioning
#!/usr/bin/env bash
# mise description="Release new version"
# mise depends=["test", "build"]
VERSION_TYPE=${1:-patch} # major, minor, or patch
echo "Creating $VERSION_TYPE release..."
npm version $VERSION_TYPE
git push --follow-tags
npm publish
Changelog Generation
[tasks.changelog]
run = '''
echo "# Changelog" > CHANGELOG.md
git log --pretty=format:"- %s (%h)" >> CHANGELOG.md
'''
Release Workflow
[tasks.release]
depends = ["release:prepare", "release:publish", "release:announce"]
[tasks."release:prepare"]
run = [
"mise run test",
"mise run build",
"mise run changelog",
"git add .",
"git commit -m 'chore: prepare release'"
]
[tasks."release:publish"]
run = [
"npm version patch",
"git push --follow-tags",
"npm publish"
]
[tasks."release:announce"]
run = "./scripts/announce-release.sh"
CI/CD Patterns
Local CI Simulation
[tasks."ci:local"]
description = "Simulate CI pipeline locally"
run = [
"mise run clean",
"mise run install",
"mise run lint",
"mise run type-check",
"mise run test",
"mise run build"
]
Fast Feedback Loop
[tasks."ci:quick"]
description = "Quick checks (< 1 min)"
depends = ["lint", "type-check", "test:unit"]
[tasks."ci:full"]
description = "Full CI pipeline (~ 5 min)"
depends = ["ci:quick", "test:integration", "test:e2e", "build"]
Artifact Generation
#!/usr/bin/env bash
# mise description="Generate CI artifacts"
# mise depends=["test", "build"]
mkdir -p artifacts
# Copy build output
cp -r dist artifacts/
# Copy test results
cp -r coverage artifacts/
# Create tarball
tar -czf artifacts/app-${CI_COMMIT_SHA}.tar.gz dist/
echo "Artifacts ready in ./artifacts/"
Monitoring & Health Checks
Health Check Pattern
#!/usr/bin/env bash
# mise description="Health check"
response=$(curl -sf http://localhost:3000/health)
if [ $? -eq 0 ]; then
echo "✓ Health check passed"
exit 0
else
echo "✗ Health check failed"
exit 1
fi
Smoke Tests
[tasks."smoke:staging"]
env = { API_URL = "https://staging.example.com" }
run = "./scripts/smoke-tests.sh"
[tasks."smoke:production"]
env = { API_URL = "https://api.example.com" }
run = "./scripts/smoke-tests.sh"
Development Workflow Patterns
Clean Slate
[tasks.reset]
description = "Reset project to clean state"
run = [
"rm -rf node_modules dist coverage .turbo",
"npm install",
"mise run db:reset",
"mise run build"
]
Quick Start
[tasks.start]
description = "Quick start (assumes deps installed)"
depends = ["db:migrate"]
run = "npm run dev"
[tasks.setup]
description = "Full setup from scratch"
run = [
"npm install",
"cp .env.example .env",
"mise run db:reset",
"mise run start"
]
Development with Live Reload
[tasks.dev]
description = "Development with live reload"
env = { NODE_ENV = "development" }
run = "tsx watch src/index.ts"
[tasks."dev:debug"]
description = "Development with debugger"
env = { NODE_ENV = "development", DEBUG = "*" }
run = "tsx watch --inspect src/index.ts"
Best Practices
- Idempotent Tasks: Tasks should be safe to run multiple times
- Fast Feedback: Prioritize quick checks before slow ones
- Clear Output: Provide informative messages about progress
- Error Handling: Use proper exit codes and error messages
- Documentation: Include descriptions for all tasks
- Composability: Build complex workflows from simple tasks
- Environment Awareness: Use environment variables for configuration
Anti-Patterns
❌ Don’t: Chain Tasks with Semicolons
[tasks.ci]
run = "npm run lint; npm test; npm run build"
Why: Errors are ignored, tasks continue even on failure
✅ Do: Use Array or Dependencies
[tasks.ci]
run = [
"npm run lint",
"npm test",
"npm run build"
]
❌ Don’t: Hardcode Environment Values
[tasks.deploy]
run = "deploy.sh --env production --region us-east-1"
✅ Do: Use Environment Variables
[tasks.deploy]
env = { ENV = "production", REGION = "us-east-1" }
run = "deploy.sh --env $ENV --region $REGION"
❌ Don’t: Duplicate Logic
[tasks.test-api]
run = "cd api && npm test"
[tasks.test-web]
run = "cd web && npm test"
✅ Do: Use Monorepo Tasks
[tasks.test]
run = "npm test"
# Call from root: mise run -C api test
Tool Installation
Comprehensive guide to installing and managing tool versions with Mise.
Basic Tool Installation
Installing Tools
# Install specific version
mise install node@20.10.0
mise install python@3.12.0
mise install rust@1.75.0
# Install latest version
mise install node@latest
mise install python@latest
# Install from .tool-versions or mise.toml
mise install
Setting Tool Versions
# Set global version
mise use --global node@20
# Set project version
mise use node@20.10.0
mise use python@3.12 rust@1.75
# Use latest
mise use node@latest
Tool Configuration in mise.toml
Basic Tool Definitions
# mise.toml
[tools]
node = "20.10.0"
python = "3.12.0"
rust = "1.75.0"
terraform = "1.6.0"
Version Prefixes
[tools]
# Latest patch version
node = "20.10"
# Latest minor version
node = "20"
# Latest version
node = "latest"
# Prefix notation
terraform = "1.6" # Latest 1.6.x
Multiple Versions
[tools]
# Use multiple versions
node = ["20.10.0", "18.19.0"]
python = ["3.12", "3.11", "3.10"]
# Switch between versions
mise shell node@18.19.0
Tool-Specific Configuration
Node.js Configuration
[tools]
node = { version = "20.10.0", postinstall = "corepack enable" }
Python with Virtual Environments
[tools]
python = "3.12"
[env]
_.python.venv = { path = ".venv", create = true }
Custom Tool Sources
[tools]
# From specific registry
"cargo:eza" = "latest"
"npm:typescript" = "5.3"
# From git repository
my-tool = "git:https://github.com/org/tool.git"
Supported Languages & Tools
Core Tools
[tools]
# Languages
bun = "1.0"
deno = "1.38"
elixir = "1.15"
erlang = "26.1"
go = "1.21"
java = "21"
node = "20.10"
python = "3.12"
ruby = "3.3"
rust = "1.75"
zig = "0.11"
# Infrastructure
terraform = "1.6"
kubectl = "1.28"
awscli = "2.13"
Package Managers
[tools]
"npm:pnpm" = "8.10"
"npm:yarn" = "4.0"
"cargo:cargo-binstall" = "latest"
"go:github.com/golangci/golangci-lint/cmd/golangci-lint" = "latest"
Tool Verification
Check Installed Tools
# List installed tools
mise list
# Check current versions
mise current
# Verify tool installation
mise doctor
Tool Information
# Show tool details
mise ls-remote node
# List available versions
mise ls-remote python
# Check latest version
mise latest node
Tool Version Strategies
Lock to Specific Versions
# Production: Pin exact versions
[tools]
node = "20.10.0"
terraform = "1.6.4"
Use Ranges for Flexibility
# Development: Use minor version ranges
[tools]
node = "20" # Any 20.x
python = "3.12" # Any 3.12.x
Latest for Experimentation
# Experimental projects
[tools]
rust = "latest"
bun = "latest"
Managing Tool Aliases
Creating Aliases
# Set alias for current directory
mise alias set node lts 20.10.0
# Set global alias
mise alias set --global python3 python@3.12
Using Aliases in Configuration
[tools]
node = "lts"
python = "3.12"
Advanced Patterns
Conditional Tool Installation
[tools]
# Install based on platform
node = "20.10.0"
python = "3.12"
# Platform-specific tools
[tools."cargo:watchexec-cli"]
platforms = ["linux", "darwin"]
version = "latest"
Tool Installation Hooks
[tools]
node = {
version = "20.10.0",
postinstall = '''
corepack enable
npm install -g npm@latest
'''
}
Backend Selection
# Use specific backend for tools
[tools]
# Use core backend (faster)
node = "core:20.10.0"
# Use asdf plugin
ruby = "asdf:3.3.0"
Tool Updates Strategy
# Check for updates
mise outdated
# Update to latest patch version
mise upgrade node
# Update all tools
mise upgrade
# Update with constraints
mise use node@20 # Updates to latest 20.x
Best Practices
Pin Production Dependencies
# Good: Explicit production versions
[tools]
node = "20.10.0"
terraform = "1.6.4"
postgres = "16.1"
Document Required Tools
# mise.toml - All project dependencies in one place
[tools]
node = "20.10.0"
python = "3.12.0"
terraform = "1.6.4"
kubectl = "1.28.0"
[env]
PROJECT_NAME = "my-app"
Use Tool-Specific Settings
[tools]
# Enable corepack for package managers
node = { version = "20.10.0", postinstall = "corepack enable" }
# Create Python virtual environment
python = { version = "3.12", venv = ".venv" }
Verify Tool Installation
# In CI/CD pipelines
mise install --check
mise doctor
# Verify specific tools
mise current node
mise current python
Anti-Patterns
Don’t Mix Version Managers
# Bad: Using multiple version managers
nvm use 20
mise use node@20 # Conflicts
# Good: Use only Mise
mise use node@20
Don’t Hardcode Tool Paths
# Bad: Hardcoded paths
/Users/me/.local/share/mise/installs/node/20.10.0/bin/node
# Good: Use mise shims or mise exec
mise exec -- node
mise x -- node
Don’t Skip Version Constraints
# Bad: No version specified
[tools]
node = "latest" # Can break on updates
# Good: Specify constraints
[tools]
node = "20.10.0" # Explicit
# OR
node = "20" # Controlled range
Don’t Ignore Tool Dependencies
# Bad: Missing required tools
[tools]
terraform = "1.6"
# Missing: kubectl, helm for deployment
# Good: Include all dependencies
[tools]
terraform = "1.6.4"
kubectl = "1.28.0"
helm = "3.13.0"
Migration from Other Version Managers
Migrating to Mise from asdf, nvm, rbenv, pyenv, and other version managers.
From asdf
Reading Existing Configuration
# Mise automatically reads .tool-versions
cat .tool-versions
# nodejs 20.10.0
# python 3.12.0
# Install and use
mise install
mise current
Full Migration
# 1. Install mise
# (see mise documentation)
# 2. Migrate configuration
mise use node@20.10.0 python@3.12.0
# 3. Remove asdf (optional)
rm -rf ~/.asdf
# Remove asdf from shell config
Plugin Compatibility
Most asdf plugins work with mise:
# List asdf plugins
asdf plugin list
# Install same plugins in mise
mise plugins install postgres
mise use postgres@16.1
From nvm (Node Version Manager)
Reading .nvmrc Files
# Existing .nvmrc
cat .nvmrc
# 20.10.0
# Migrate to mise
mise use node@$(cat .nvmrc)
Migration Steps
# 1. Note current nvm version
nvm current
# v20.10.0
# 2. Install same version with mise
mise use node@20.10.0
# 3. Remove nvm (optional)
rm -rf ~/.nvm
# Remove nvm from shell config
Global Package Migration
# Export nvm global packages
npm list -g --depth=0 > npm-global.txt
# After switching to mise
mise use node@20.10.0
# Reinstall global packages
From rbenv (Ruby Version Manager)
Reading .ruby-version Files
# Existing .ruby-version
cat .ruby-version
# 3.3.0
# Migrate to mise
mise use ruby@$(cat .ruby-version)
Migration Steps
# 1. Note current rbenv version
rbenv version
# 3.3.0
# 2. Install same version with mise
mise use ruby@3.3.0
# 3. Remove rbenv (optional)
rm -rf ~/.rbenv
# Remove rbenv from shell config
From pyenv (Python Version Manager)
Reading .python-version Files
# Existing .python-version
cat .python-version
# 3.12.0
# Migrate to mise
mise use python@$(cat .python-version)
Migration Steps
# 1. Note current pyenv version
pyenv version
# 3.12.0
# 2. Install same version with mise
mise use python@3.12.0
# 3. Preserve virtual environments
# Mise can use existing venvs
[tools]
python = "3.12"
[env]
_.python.venv = { path = ".venv", create = true }
# 4. Remove pyenv (optional)
rm -rf ~/.pyenv
# Remove pyenv from shell config
From sdkman (Java/JVM Version Manager)
Migration Steps
# 1. Note current SDK versions
sdk current
# 2. Install with mise
mise use java@21 gradle@8.5 maven@3.9
# 3. Remove sdkman (optional)
rm -rf ~/.sdkman
# Remove sdkman from shell config
From direnv
Environment Variable Migration
# Old .envrc
export DATABASE_URL=postgresql://localhost/myapp
export NODE_ENV=development
# New mise.toml
[env]
DATABASE_URL = "postgresql://localhost/myapp"
NODE_ENV = "development"
Migration Steps
# 1. Convert .envrc to mise.toml
# (manual conversion of export statements)
# 2. Mise automatically activates on directory change
# 3. Remove .envrc (optional)
rm .envrc
# 4. Keep direnv for other projects (optional)
# Mise and direnv can coexist
Migration Checklist
Pre-Migration
- List current tool versions
- Export global packages/gems/modules
- Document custom configurations
- Backup shell configuration files
During Migration
- Install mise
- Configure shell activation
- Migrate tool versions to mise.toml
- Test tool availability
- Reinstall global packages
Post-Migration
- Remove old version managers (optional)
- Clean up shell configuration
- Update CI/CD pipelines
- Document changes for team
Multi-Tool Migration Example
# Before: Multiple version managers
nvm use 20.10.0
rbenv local 3.3.0
pyenv local 3.12.0
# After: Single mise configuration
# mise.toml
[tools]
node = "20.10.0"
ruby = "3.3.0"
python = "3.12.0"
Team Migration Strategy
Gradual Migration
# Phase 1: Add mise.toml alongside existing files
# Keep .nvmrc, .ruby-version, .python-version
[tools]
node = "20.10.0"
ruby = "3.3.0"
python = "3.12.0"
# Phase 2: Team adopts mise
# (Keep legacy files for stragglers)
# Phase 3: Remove legacy files
# Once entire team migrated
CI/CD Migration
# Before: Multiple version managers in CI
- uses: actions/setup-node@v3
with:
node-version: '20.10.0'
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3.0'
# After: Single mise setup
- uses: jdx/mise-action@v2
# Automatically uses versions from mise.toml
Troubleshooting Migration
Shell Not Activating
# Ensure mise activation in shell config
# ~/.bashrc or ~/.zshrc
eval "$(mise activate bash)" # or zsh
Tool Not Found After Migration
# Verify installation
mise current
# Reinstall if needed
mise install
Global Packages Missing
# Node.js
npm install -g package-name
# Python
pip install package-name
# Ruby
gem install gem-name
Version Conflicts
# Check which version manager is active
which node
# Should point to mise shim
# If conflicts exist
mise doctor
Best Practices
Keep Legacy Files During Transition
# Support both mise and nvm during migration
.nvmrc # For nvm users
mise.toml # For mise users
Document Migration for Team
Create MIGRATION.md:
# Migration to Mise
## Installation
1. Install mise: https://mise.jdx.dev/getting-started.html
2. Add to shell: `eval "$(mise activate bash)"`
3. Install tools: `mise install`
## Support
Contact #engineering-tools for help
Test Before Removing Old Managers
# 1. Install mise and test
mise install
mise current
# 2. Verify application works
npm test
npm run build
# 3. Only then remove old version managers
Tool Plugins
Understanding Mise’s plugin system and available tool plugins.
Core vs Plugin Tools
Core Tools (Built-in)
Mise has built-in support for popular tools (no plugins needed):
[tools]
# Core tools - faster, no plugin installation
node = "20.10.0"
python = "3.12.0"
java = "21"
go = "1.21"
rust = "1.75"
Plugin Tools
Other tools use the asdf plugin ecosystem:
# List available plugins
mise plugins ls-remote
# Install a plugin
mise plugins install postgres
# Use the plugin
mise use postgres@16.1
Common Plugin Tools
Databases
[tools]
postgres = "16.1"
mysql = "8.2"
redis = "7.2"
mongodb = "7.0"
Cloud CLI Tools
[tools]
awscli = "2.13"
gcloud = "450.0"
kubectl = "1.28"
helm = "3.13"
Build Tools
[tools]
cmake = "3.27"
make = "4.4"
bazel = "7.0"
Package Manager Backends
npm Backend
Install npm packages as tools:
[tools]
"npm:typescript" = "5.3"
"npm:prettier" = "3.1"
"npm:eslint" = "8.55"
cargo Backend
Install cargo packages as tools:
[tools]
"cargo:eza" = "latest"
"cargo:ripgrep" = "latest"
"cargo:fd-find" = "latest"
go Backend
Install Go tools:
[tools]
"go:github.com/golangci/golangci-lint/cmd/golangci-lint" = "latest"
Plugin Management
Installing Plugins
# Auto-install when using tool
mise use postgres@16
# Manual plugin installation
mise plugins install postgres
# Install from custom URL
mise plugins install myplugin https://github.com/user/mise-myplugin
Updating Plugins
# Update all plugins
mise plugins update
# Update specific plugin
mise plugins update node
# List installed plugins
mise plugins ls
Removing Plugins
# Uninstall plugin
mise plugins uninstall postgres
Custom Plugins
Using Custom Plugin Repositories
# Install from GitHub
mise plugins install custom https://github.com/org/mise-custom
# Use custom plugin
mise use custom@1.0.0
Plugin Configuration
[plugins]
# Configure plugin behavior
postgres = { repo = "https://github.com/custom/mise-postgres" }
Backend Priority
When a tool is available from multiple sources:
[tools]
# 1. Core backend (fastest, preferred)
node = "20.10.0"
# 2. Explicit backend selection
node = "core:20.10.0"
python = "core:3.12.0"
# 3. Plugin backend
ruby = "asdf:3.3.0"
# 4. Package manager backend
"npm:typescript" = "5.3"
Best Practices
Prefer Core Tools
# Good: Use core tools when available
[tools]
node = "20.10.0"
python = "3.12.0"
# Avoid: Explicitly using plugin backend
[tools]
node = "asdf:20.10.0" # Unnecessary
Pin Plugin Versions in CI
# In CI/CD, ensure consistent plugin versions
mise plugins install --sha 7c839f8 postgres
Document Plugin Requirements
# mise.toml
[tools]
# Requires postgres plugin
postgres = "16.1"
# Core tool, no plugin needed
node = "20.10.0"
Troubleshooting
Plugin Not Found
# Error: Plugin not found
mise use unfamiliar-tool@1.0
# Solution: Install plugin first
mise plugins install unfamiliar-tool
mise use unfamiliar-tool@1.0
Plugin Installation Fails
# Check plugin repository
mise plugins ls-remote | grep postgres
# Try manual installation
mise plugins install postgres
# Check plugin status
mise doctor
Version Not Available
# Check available versions
mise ls-remote node
# Try different version
mise use node@20.10.0
Tool Versions File Format
Understanding and using .tool-versions files with Mise.
File Format
Basic Format
# .tool-versions
nodejs 20.10.0
python 3.12.0
rust 1.75.0
terraform 1.6.0
Version Specifications
# Exact version
nodejs 20.10.0
# Latest patch version
nodejs 20.10
# Latest minor version
nodejs 20
# Latest version
nodejs latest
Comments
# Comments start with #
nodejs 20.10.0 # End-of-line comments also supported
Compatibility with asdf
Mise reads .tool-versions files for backward compatibility with asdf:
# Existing .tool-versions file
cat .tool-versions
# nodejs 20.10.0
# python 3.12.0
# Mise automatically uses it
mise install
mise current
Migration from .tool-versions to mise.toml
Simple Migration
# Existing .tool-versions
cat .tool-versions
# nodejs 20.10.0
# python 3.12.0
# Migrate to mise.toml
mise use node@20.10.0 python@3.12.0
# This creates mise.toml:
# [tools]
# node = "20.10.0"
# python = "3.12.0"
Using Both Files
# mise.toml can coexist with .tool-versions
# Mise prioritizes mise.toml over .tool-versions
# .tool-versions
nodejs 20.10.0
# mise.toml (takes precedence)
[tools]
node = "20.11.0" # This version is used
Legacy Version Files
Mise also reads legacy version files from other version managers:
# .nvmrc for nvm
cat .nvmrc
# 20.10.0
# .ruby-version for rbenv
cat .ruby-version
# 3.3.0
# .python-version for pyenv
cat .python-version
# 3.12.0
Best Practices
Use mise.toml for New Projects
# Prefer mise.toml over .tool-versions
[tools]
node = "20.10.0"
python = "3.12.0"
Benefits:
- More flexible configuration options
- Environment variables in same file
- Task definitions in same file
- Tool-specific settings
Keep .tool-versions for Compatibility
Keep .tool-versions if your team uses asdf:
# .tool-versions - for asdf users
nodejs 20.10.0
python 3.12.0
# mise.toml - for mise users
[tools]
node = "20.10.0"
python = "3.12.0"
Document Version Choices
# .tool-versions
# Node.js 20 LTS for production compatibility
nodejs 20.10.0
# Python 3.12 for async improvements
python 3.12.0
# Terraform 1.6 for HCL2 features
terraform 1.6.4
Anti-Patterns
Don’t Mix Formats Carelessly
# Bad: Conflicting versions
# .tool-versions
nodejs 20.10.0
# mise.toml
[tools]
node = "18.19.0" # Confusion!
Don’t Use Vague Versions in CI
# Bad: Unpredictable CI builds
nodejs latest
python 3
# Good: Explicit versions
nodejs 20.10.0
python 3.12.1