How to Install OpenTofu on macOS, Linux, and Windows
Step-by-step guide to install OpenTofu on macOS (Homebrew), Linux (apt, dnf, rpm), and Windows (Chocolatey, Scoop). Includes verification, version pinning...
OpenTofu
OpenTofu's early evaluation lets you use variables and locals in backend configuration, module sources, and required_providers — features Terraform doesn't...
Early variable evaluation is OpenTofu's answer to one of Terraform's
oldest pain points: backend blocks, module sources, and required_providers
versions cannot reference variables or locals. OpenTofu lifts this
restriction, enabling fully parameterised, environment-driven configuration
without code generation or terragrunt.
In Terraform you cannot write:
# Terraform: ERROR — variables not allowed in backend
terraform {
backend "s3" {
bucket = var.state_bucket
key = "${var.env}/terraform.tfstate"
region = var.region
}
}The standard workaround is partial config + -backend-config= files:
terraform init \
-backend-config="bucket=tfstate-prod" \
-backend-config="key=prod/terraform.tfstate" \
-backend-config="region=eu-west-1"Functional, but verbose, hard to template, and unfriendly to GitOps.
OpenTofu evaluates variables and locals early enough for backend, module, and provider blocks to consume them:
variable "env" { type = string }
variable "state_bucket" { type = string }
variable "region" { type = string default = "eu-west-1" }
terraform {
backend "s3" {
bucket = var.state_bucket
key = "${var.env}/terraform.tfstate"
region = var.region
}
}Run with:
tofu init -var="env=prod" -var="state_bucket=tfstate-prod"
tofu planOr via a tfvars file:
# prod.tfvars
env = "prod"
state_bucket = "tfstate-prod"
region = "eu-west-1"tofu init -var-file=prod.tfvarsA common Terraform anti-pattern is duplicating wrapper modules per environment. With OpenTofu:
variable "module_version" { type = string default = "1.4.0" }
module "vpc" {
source = "git::ssh://git@github.com/acme/tf-modules.git//vpc?ref=${var.module_version}"
cidr = "10.0.0.0/16"
}Roll forward by bumping module_version in a tfvars file — no code change.
required_providersPin different provider versions per environment without forking the module:
variable "aws_provider_version" {
type = string
default = "~> 5.70"
}
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = var.aws_provider_version
}
}
}Useful for canarying a new provider release in staging while prod stays
on the known-good version.
Early evaluation works for:
terraform.backend.<type> argumentsmodule.<name>.source and versionterraform.required_providers.<name>.source and versionterraform.cloud (HCP Terraform integration)It does not work for:
The constraint: early-evaluated values must come from variable defaults,
-var/-var-file, environment variables (TF_VAR_*), or pure locals
that don't reference resources/data sources.
Before (Terraform):
terraform {
backend "s3" {} # everything in -backend-config files
}terraform init -backend-config=envs/prod.s3.tfbackendAfter (OpenTofu):
terraform {
backend "s3" {
bucket = var.state_bucket
key = var.state_key
region = var.region
}
}tofu init -var-file=envs/prod.tfvarsA single *.tfvars per environment now drives everything — backend,
module versions, providers, resources. No more shadow .tfbackend files.
variable "env" { type = string }
variable "module_version" { type = string }
variable "aws_version" { type = string default = "~> 5.70" }
locals {
state_bucket = "tfstate-${var.env}"
state_key = "${var.env}/network/terraform.tfstate"
}
terraform {
required_version = ">= 1.8.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = var.aws_version
}
}
backend "s3" {
bucket = local.state_bucket
key = local.state_key
region = "eu-west-1"
encrypt = true
}
}
module "vpc" {
source = "git::ssh://git@github.com/acme/tf-modules.git//vpc?ref=${var.module_version}"
env = var.env
}One module, one workflow, every environment.
If you ever plan to migrate back to HashiCorp Terraform, keep the
backend block static — Terraform 1.x will reject var.* references with a
parse error. Many teams gate this with feature flags or maintain a
backend.terraform.tf and backend.opentofu.tf pair. See
Terraform vs OpenTofu: Key Differences
for the full divergence list.
Early evaluation removes the single most-asked-for missing feature in Terraform. Combined with state encryption and an open registry, it makes OpenTofu the more flexible choice for teams that have hit the limits of partial backend config and per-environment file duplication.
Step-by-step guide to install OpenTofu on macOS (Homebrew), Linux (apt, dnf, rpm), and Windows (Chocolatey, Scoop). Includes verification, version pinning...
How OpenTofu resolves providers from registry.opentofu.org, configures the registry, sets up filesystem mirrors for air-gapped environments, and caches...
Configure OpenTofu's built-in state encryption to protect sensitive values at rest. AES-GCM with PBKDF2 or AWS KMS / GCP KMS / Azure Key Vault key providers.
Fix the Terraform 'Reference to undeclared resource' error. Causes include typos, missing resources, wrong module references, and for_each/count confusion.