In August 2023 HashiCorp relicensed Terraform from the open-source MPL to the Business Source License (BSL), which restricts competing commercial use. The community responded by forking the last MPL version into OpenTofu, now a Linux Foundation project under a permissive license. OpenTofu is a deliberate drop-in replacement, which makes this the lowest-risk migration on this site: for most teams it’s a tooling swap, not a rewrite.
Why teams move
The driver is licensing, not features. Vendors and platforms that build on Terraform faced redistribution risk under the BSL; many users simply want to stay on genuinely open-source IaC. OpenTofu keeps the same HCL language, the same workflow, and a compatible state format, so the cost of switching is low and the cost of not switching is mostly governance and supply-chain risk.
There is a second, quieter driver: cost at scale. HCP Terraform prices on a per-resource basis, so large estates can see the bill climb as managed resource counts grow. OpenTofu itself is a Linux Foundation project with no per-resource licensing, which removes that variable entirely. If you are running your own state backend and CI, the ongoing cost of OpenTofu is the infrastructure you already own. Use the calculator above to compare your current HCP posture against a self-hosted OpenTofu setup before you assume the savings, and confirm any commercial-support arrangements separately, since community-governed does not mean vendor-supported.
What stays the same
- The language. Your
.tffiles, modules, theterraform {}block, and provider/resource syntax are unchanged. - State. OpenTofu reads existing Terraform state; the format is compatible.
- Providers and modules. OpenTofu uses its own public registry but resolves the same providers (the major clouds, Kubernetes, etc.) and modules.
- The workflow.
init,plan,apply,destroy, same commands, under thetofubinary.
The migration, step by step
- Install the
tofuCLI and pin a specific version in CI and in developer tooling. - Back up state (or rely on remote-backend versioning) before the first run, standard hygiene.
- Run
tofu initin a workspace, thentofu planand confirm it reports no changes against current infrastructure. A clean, empty diff is your proof of parity. - Update CI/CD to call
tofuinstead ofterraform, and refresh any wrappers (Atlantis, Terragrunt, pipelines) to the OpenTofu binary. - Apply through the new pipeline and watch the first few runs closely.
Because state and HCL are compatible, there is no data migration, the work is swapping the binary everywhere it’s invoked and proving plan parity.
Handing state over without moving it
The phrase “migrating state” undersells what happens here, because the whole point of the fork is that the state format carries over untouched. What actually moves is which binary is authoritative for that state. The safe sequence is: freeze changes to the workspace, run tofu init against the existing backend so OpenTofu records itself as the tool of record, run tofu plan, and read the diff carefully. A truly empty diff, no adds, no changes, no destroys, is the signal that OpenTofu sees your infrastructure exactly as Terraform did.
If the plan is not empty, do not apply it. A non-empty diff usually means one of three things: a provider resolved to a different version under OpenTofu’s registry, a lock file that was not regenerated, or HCL that used a Terraform-version-specific behavior. Investigate the diff line by line rather than accepting it. For remote backends, the state object itself does not need to be copied; both tools read the same backend. For local state, back up the .tfstate file first as a matter of course, even though you expect not to need it.
The three buckets
To keep the two columns clear:
- Unchanged: your HCL, modules, the resource graph, remote backend configuration, and the state file itself.
- Changed: the binary name (
tofuin place ofterraform), the provider lock file and mirrors, and every place your CI, wrappers, and developer docs invoke the tool. - Watch closely: version pins on both the CLI and providers, and any workflow that assumed a Terraform-only feature.
Version drift is the one real catch
OpenTofu forked from Terraform around the 1.5/1.6 line and the two have diverged since. OpenTofu added features such as state encryption, early variable evaluation, and provider-defined functions on its own timeline; Terraform has added features on its. Practical implications:
- Pin versions on both sides. Don’t let a
terraformblock’srequired_versionor a provider lock drift between tools. - Mind the lock file.
.terraform.lock.hcland provider mirrors should be regenerated under OpenTofu so hashes match its registry. - Avoid mixing tools on one state. Pick OpenTofu per workspace and stay there; round-tripping between newer Terraform and OpenTofu can introduce features the other can’t read.
- Terraform Cloud/HCP users: OpenTofu works with most standard backends, but HCP/TFC-specific workflows differ, plan that backend separately.
CI/CD and validation
Most of the real work lives in the pipeline, because that is where the binary is invoked dozens of times a day. Inventory every place the string terraform appears: pipeline steps, Makefiles, pre-commit hooks, wrapper tools like Terragrunt or Atlantis, and any container image that bakes in the CLI. Swap each to the pinned tofu binary. Pinning matters more than usual here: because the forks diverge, an unpinned CLI can silently pull a newer OpenTofu that introduces a feature your state or teammates cannot read.
A low-risk way to prove parity before you commit is to run OpenTofu alongside Terraform in CI for a cycle. Have the pipeline generate a tofu plan next to the existing terraform plan and diff the two. When the OpenTofu plan is a consistent no-op across several runs and several workspaces, flip the default. This gives you evidence rather than faith, and it surfaces provider or lock-file drift while the incumbent is still authoritative.
Backing out
Rollback is about as simple as it gets on this path. Because the state format is shared, reverting is a matter of pointing CI and developers back at the terraform binary and the previous lock file. There is no state to un-migrate. Keep the pre-cutover state snapshot and the original .terraform.lock.hcl until you have several clean OpenTofu applies in production, then retire them.
A note on the siblings
The same BSL change hit Vault and Consul, whose open forks are OpenBao (Vault) and community alternatives for Consul. If you adopted OpenTofu for licensing reasons, it’s worth auditing the rest of your HashiCorp footprint for the same exposure.
The short version
Terraform → OpenTofu is mostly mechanical: install the binary, pin versions, run tofu plan to confirm an empty diff, and swap CI/CD over. The only thing to watch is version drift between the two forks, pin deliberately and don’t straddle both on one state. There’s no per-resource licensing to model here, but use the calculator above to weigh the broader IaC and secrets footprint, and confirm any commercial support assumptions before you commit.