Migrating from tfenv to mise — reading .terraform-version, translating tfenv-installed versions, handling the terraform vs tofu fork decision, and tearing down ~/.tfenv cleanly. Use when a user is currently using tfenv for Terraform version management.
tfenv is a shell-based Terraform version manager, modeled after rbenv/pyenv. mise replaces it with a version-pinned, aqua-backed terraform install that cross-integrates with every other tool you use. Migration is straightforward because .terraform-version has exactly one format (a bare version string).
Before migrating, decide which tool you actually want:
mise supports both:
[tools]
terraform = "1.9.8"
# or
opentofu = "1.8.4"
Most users want terraform still. opentofu matters if you're in a regulated/legal situation where the BSL is a problem, or if you want to commit to the open-source fork.
For this guide we'll assume terraform. The migration is identical for opentofu — just swap the tool name.
mise.toml per project.tfenv list # installed versions
tfenv version-name # current
cat .terraform-version # project pin
grep -r tfenv ~/.zshrc ~/.bashrc 2>/dev/null
For a .terraform-version containing 1.9.8:
# mise.toml
[tools]
terraform = "1.9.8"
Or enable the idiomatic reader and let .terraform-version work directly:
[settings]
idiomatic_version_file_enable_tools = ["terraform"]
# ~/.config/mise/config.toml
[tools]
terraform = "1.9"
Remove tfenv:
# ~/.zshrc — DELETE
export PATH="$HOME/.tfenv/bin:$PATH"
Add mise:
eval "$(mise activate zsh)"
Restart shell.
which terraform
# should be under ~/.local/share/mise/
terraform --version
cd <project>
terraform --version # matches .terraform-version
rm -rf ~/.tfenv
# If installed via Homebrew:
brew uninstall tfenv
Restart shell. Verify which tfenv → not found.
Terraform is rarely used alone. Pair with:
[tools]
terraform = "1.9.8"
"aqua:terraform-linters/tflint" = "latest"
"aqua:terraform-docs/terraform-docs" = "latest"
"aqua:aquasecurity/tfsec" = "latest"
"aqua:gruntwork-io/terragrunt" = "latest"
[tasks.init]
run = "terraform init"
sources = ["*.tf", ".terraform.lock.hcl"]
[tasks.plan]
depends = ["init"]
run = "terraform plan -out=tfplan"
[tasks.apply]
run = "terraform apply tfplan"
[tasks.lint]
run = ["tflint", "tfsec ."]
[tasks.docs]
run = "terraform-docs markdown . > README.md"
All of these are aqua-backed — fast, pre-built, version-pinned.
cd into projectCheck that the idiomatic reader is enabled, or put terraform = "<version>" in the project's mise.toml explicitly.
terraform init downloads providers to the wrong placeterraform init creates .terraform/ in the project dir. This is terraform's behavior, not tfenv's or mise's. It's already gitignored by default in most terraform .gitignore templates.
If you were using TF_PLUGIN_CACHE_DIR with tfenv, the same env var still works:
[env]
TF_PLUGIN_CACHE_DIR = "{{env.HOME}}/.cache/terraform-plugin-cache"
Shared across projects; saves bandwidth and disk.
[tools]
terraform = "1.9 1.5"
Both installed. terraform resolves to 1.9; explicit terraform_1.5.x isn't a thing — use mise exec [email protected] -- terraform plan for the non-default.
Replace terraform = with opentofu =. Update CI. Update state files if needed (for 1.6+ divergence). See opentofu migration docs for state compatibility.
Reinstall tfenv:
git clone https://github.com/tfutils/tfenv.git ~/.tfenv
Your .terraform-version files are untouched.
mise-lang-node-overview — similar structure for Node (if you're also migrating from nvm).mise-migrate-from-asdf — general migration shape.mise-backends-overview — aqua vs github for terraform.opentofu.org/docs/intro/migration/.