This lesson does not tell you which to use.
That is not diplomatic evasion. The decision depends on your organisation’s requirements, existing tooling, vendor relationships and legal position — none of which this site knows, and most of which are not engineering questions. What this lesson can do is lay out precisely what differs, so the decision is made on facts rather than on whichever advocacy piece somebody read most recently.
The short version: from a Git and repository perspective they are far more similar than different. Every workflow, structural and review practice in this cluster applies to both. The differences are real, specific and enumerable.
What is identical
Section titled “What is identical”Worth establishing first, because it is most of it.
HCL. The same configuration language. Resources, variables, outputs, modules, expressions, functions, moved blocks, lifecycle blocks.
The core workflow. init, validate, plan, apply, destroy, with the same flags and the same -detailed-exitcode semantics.
State format. Compatible, which is what makes migration feasible at all — the same JSON structure, the same serial and lineage semantics, read and written identically.
Providers. The same builds, written against the same plugin SDK.
Backends. S3, Azure Storage, GCS, HTTP, Kubernetes, and the same locking model, including the same behaviour around -lock-timeout and force-unlock.
The lock file. Still .terraform.lock.hcl, still committed.
The terraform block keyword. Unchanged in OpenTofu.
Which means every practice in this cluster — repository structure, module versioning, pull request validation, state handling, environment strategy, branching — transfers without modification.
Where they diverge
Section titled “Where they diverge”| Dimension | Terraform | OpenTofu |
|---|---|---|
| Binary | terraform | tofu |
| State encryption | Backend-side only | Client-side, several key providers |
| Plan file encryption | No | Yes |
| Variables in backend config | No — partial config required | Yes |
| Variables in module sources | No | Yes |
Provider for_each | No | Yes |
for_each on import | No | Yes |
| Provider mocking in tests | No | Yes |
| File extension | .tf | .tf, plus .tofu which shadows it |
| Registry | HashiCorp | OpenTofu’s own |
| Managed service | HCP Terraform | Third-party and self-hosted options |
| Governance | Vendor-led | Linux Foundation project |
Every row marked in the OpenTofu column is a capability Terraform does not have at the time of writing. That is not an argument for OpenTofu — features you do not need are not benefits, and the direction of travel is not fixed.
The differences that affect repository engineering
Section titled “The differences that affect repository engineering”Four of those rows change how you would structure or operate a repository. The rest are conveniences.
State encryption is the significant one. It changes the answer to “who can read our infrastructure secrets” from “anyone with read access to the state bucket” to “anyone with access to the encryption key”. If state sensitivity is a live concern — a regulated environment, a large number of people with repository access, an auditor asking the question — this is a substantive difference rather than a nice-to-have. It also transfers a real operational burden: key management, key rotation, and a recovery plan for a lost key.
Plan encryption follows from it, and makes the saved-plan apply pattern from the CI/CD lesson materially easier. Being able to store a reviewed plan as an ordinary artifact without it being a secret leak is a genuine simplification.
Variables in backend configuration removes the partial-configuration workaround. Minor in isolation, and it compounds in a repository with many environment directories, each of which currently carries either hard-coded backend values or a separate -backend-config file that CI must pass correctly.
Provider for_each changes how multi-region and multi-account estates are expressed. In Terraform, N regions means N provider aliases and N module calls, frequently generated. In OpenTofu it is a list variable. For an estate spanning many accounts this is the difference between a generated directory tree and a values file.
The other rows — mocking, .tofu files, registry — are real and rarely decisive.
Managed services and ecosystem
Section titled “Managed services and ecosystem”Worth stating factually because it is a genuine input to the decision.
Terraform has HCP Terraform, HashiCorp’s managed service: hosted state, remote execution, a private module registry, policy enforcement and a run UI. A team using it has adopted a workflow that is not purely repository-plus-CI, and moving away means replacing several things at once.
OpenTofu has no first-party managed service. Several third parties offer platforms that support it, and self-hosting the components — state backend, CI, a policy engine — is the common approach. That is the architecture this cluster has described throughout, so it is not an unusual position.
Tooling support varies and is worth checking rather than assuming. Linters, security scanners, cost estimators, IDE extensions and CI actions mostly support both, and “mostly” is doing work in that sentence. If your pipeline depends on a specific tool, verify it supports the one you are considering — this is the practical compatibility question that catches teams, more often than anything about the language.
Both are in wide production use. Neither is a research project, and neither is a safe default assumption for every context — a repository that does not say which tool it targets is ambiguous in a way that was not true a few years ago.
Governance and licensing, factually
Section titled “Governance and licensing, factually”The projects have different governance models and different licences, and this is frequently the actual reason a team evaluates the fork rather than anything in the tables above.
Terraform is developed by HashiCorp under a source-available licence. It is not OSI-approved open source, and its terms restrict certain competitive uses.
OpenTofu is a Linux Foundation project under the Mozilla Public License 2.0, which is OSI-approved.
Whether that difference matters to you depends on how you use the tool, what your organisation’s policy on licensing says, whether you embed it in a product, and what your legal team advises. Those are not engineering questions and this site is not the right source for them.
Repository-level differences in practice
Section titled “Repository-level differences in practice”What a reviewer or a new joiner would actually notice when opening one repository versus the other.
The workflow files name a different binary and a different setup action. That is the most visible difference, and it is superficial.
The backend block may look different. An OpenTofu repository can carry variables in it; a Terraform repository will have partial configuration plus -backend-config in CI. The second is more moving parts and both are perfectly workable.
An encryption block may be present. This is the clearest signal you are in an OpenTofu repository, and it carries operational implications a reader should notice: there is a key somewhere, somebody owns it, and CI needs permission to use it.
Provider configuration may be iterated rather than repeated. A Terraform repository targeting eight accounts will have eight provider aliases, often in a generated file. An OpenTofu one may have a single for_each. The second is shorter and requires knowing the feature exists.
.tofu files may shadow .tf files. Rare, and worth a prominent README note where it happens, because a reader editing the wrong file and seeing no effect will lose an hour.
Everything else looks the same. Module structure, environment directories, CODEOWNERS, .gitignore, the lock file, the pull request template, the plan comment. A reviewer who knows one repository can review the other.
That last point is the practical summary of this whole lesson: the review skills transfer completely, which is why the choice matters less than the volume of discussion around it implies.
What each is likely to do next
Section titled “What each is likely to do next”Not a prediction, and worth framing honestly because the tables above are a snapshot.
Both projects are actively developed and ship releases on their own cadences. Features appear in each that the other does not have, and the direction is not one-way — Terraform continues to add capability, and OpenTofu continues to diverge deliberately.
Compatibility is a design goal for OpenTofu, not a guarantee. The stated intent is that Terraform configurations work, and the divergent feature list makes clear that the reverse is not aimed at. Expect the common subset to remain large and expect it to shrink slowly as a proportion.
The provider ecosystem is the shared foundation and the thing most likely to keep the tools compatible. Providers are where the actual work is, they are built against a common SDK, and neither project benefits from fragmenting them.
What this means for a decision made today: choose on your current requirements, not on a forecast. Both tools will do the job for the foreseeable future, the migration cost is asymmetric but bounded, and revisiting in a year is a reasonable plan rather than a failure to decide.
Choosing, by situation
Section titled “Choosing, by situation”Rather than a verdict, the situations where one is more clearly indicated.
You are starting fresh with no constraints. Either. Pick one, pin it, document it, and revisit in a year. The migration cost in either direction is currently low, and it grows as you adopt divergent features.
You use HCP Terraform and are happy with it. Stay. Moving means replacing a managed service, not swapping a binary, and the tables above do not justify that on their own.
State sensitivity is a live requirement. OpenTofu’s client-side encryption is a real capability that Terraform does not have. Weigh it against the key-management burden.
Your organisation requires OSI-approved licensing. That resolves it, and it is a policy answer rather than a technical one.
You have a large multi-account estate expressed as generated directories. Provider for_each may be worth a serious look.
Your pipeline depends on a specific vendor tool. Check its support first. This is the most common practical blocker and the least discussed.
You have hundreds of repositories and a small platform team. Migration cost dominates every other consideration. Do not migrate because of a feature list; migrate because something you need is missing.
Migration cost, honestly
Section titled “Migration cost, honestly”Both directions, so the decision is reversible-aware.
Terraform to OpenTofu is a supported path with a defined verification step: run tofu plan against existing state and confirm it is empty. Per environment, lowest first. The OpenTofu lesson has the sequence. For a straightforward estate this is a day’s work plus soak time.
OpenTofu to Terraform is not symmetric. If you have adopted encryption, variables in backends, provider for_each or .tofu files, each must be unwound before Terraform will parse the configuration. Encrypted state must be decrypted. This is a project.
The asymmetry has a practical consequence: the migration is cheap while you use only the common subset, and gets progressively more expensive as you adopt divergent features. If reversibility matters to you, that is an argument for deferring the divergent features rather than for avoiding the migration — but it should be a deliberate decision, not something you discover later.
Running both
Section titled “Running both”Some organisations will, at least temporarily.
Per-repository is workable. Pin and document in each.
Shared modules are the friction point. A module consumed by both must stay in the common subset, forgoing exactly the features most likely to be useful in shared code. The alternatives — .tofu variants, or splitting the module repository — both cost something.
CI configuration doubles, mitigated by reusable workflows with a tool parameter.
Set a direction and a date. Permanently split is the worst of the three states: two toolchains, shared code stuck at the lowest common denominator, and engineers who must remember which repository is which.
Evaluating properly
Section titled “Evaluating properly”If you are actually making this decision rather than reading about it, a sequence that produces evidence.
-
Write down what is missing today. Not what would be nice — what causes you actual problems. If the list is empty, the honest answer is that this is a licensing decision rather than a technical one, and it belongs with whoever owns that policy.
-
Check your tooling. Every linter, scanner, cost tool, IDE extension and CI action in your pipeline. This is the step that most often ends the evaluation, and it takes an afternoon.
-
Pick your most representative repository, not your simplest. A repository with one root module and no modules proves nothing about your estate.
-
Run the empty-plan check against a non-production environment’s real state. This is the single piece of evidence that matters most.
-
Run your full CI pipeline against the migrated repository. Not just plan — the linter, the scanner, the policy checks, the comment posting.
-
Estimate the fleet cost. Number of repositories, environments per repository, and who does the work. Multiply honestly.
-
Decide, write down why, and set a review date. A decision record explaining the reasoning is what stops this being re-litigated every quarter by somebody who read a new blog post.
Steps 2 and 4 do most of the work. A team that has completed both knows whether the migration is feasible; everything else is scheduling and preference.
What not to do: run the evaluation on a toy repository, conclude it works, and discover the tooling gap during the fleet migration. The toy repository always works — that is what makes it a poor test.
Common mistakes
Section titled “Common mistakes”Choosing on advocacy. Both projects have enthusiastic partisans. Neither community’s tone is evidence about which fits your requirements.
Assuming a rename. sed s/terraform/tofu/ misses both the incompatibilities and the reasons to have migrated.
Migrating without the empty-plan check. The only evidence the two read your state identically.
Not checking your tooling first. The commonest practical blocker.
Adopting divergent features during the migration. Two things to debug at once, and reversibility lost before you have confirmed the migration worked.
Treating the feature table as a scoreboard. Features you do not need are not benefits.
Publishing a shared module using divergent features without saying so. Consumers of the other tool fail at init.
Assuming today’s gap is permanent. Both projects ship. This page will age.
The team question
Section titled “The team question”The dimension that gets least attention and often decides the outcome.
Hiring and familiarity. Terraform has a far larger installed base and a correspondingly larger pool of people who have used it. An engineer joining a Terraform repository needs no orientation; one joining an OpenTofu repository needs a paragraph, because everything they know transfers and the binary name does not match their muscle memory.
Documentation and search results. The overwhelming majority of tutorials, Stack Overflow answers and blog posts say terraform. Almost all of them apply to OpenTofu unchanged, and an engineer debugging at 2am does not want to be translating. This is a small, persistent tax rather than a blocker.
Internal standardisation. An organisation running both pays a coordination cost forever. The value of everybody using the same thing is real and frequently underweighted in these comparisons, which tend to be framed as a technical merit contest.
Vendor relationships. Organisations with an existing HashiCorp commercial relationship, support agreement or enterprise deployment are making a different decision from a team choosing freely, and pretending otherwise is not useful.
The engineer who has to own it. A capability that nobody on the team wants to operate is not an asset. State encryption is a real feature and it needs somebody who will own key rotation and a recovery plan. If that person does not exist, adopting it makes things worse rather than better.
None of these appear in a feature table, and collectively they decide more of these evaluations than the feature table does.
Mental model
Section titled “Mental model”They are the same tool for the purposes of this cluster, and different tools for the purposes of state encryption, backend configuration and provider iteration. Everything about Git, repositories, review and promotion is shared.
Which means the decision is narrower than the discussion around it suggests. You are not choosing a workflow — you already have one and it works with either. You are choosing a binary, a licence, an ecosystem and a small set of capabilities.
What you learned
Section titled “What you learned”- HCL, the core workflow, state format, providers, backends and the lock file are shared
- OpenTofu’s divergent capabilities: client-side state and plan encryption, early variable evaluation, provider
for_each,importfor_each, provider mocking,.tofufiles - State encryption is the difference most likely to affect repository engineering, and it transfers a key-management burden
- Terraform has a first-party managed service; OpenTofu’s ecosystem is third-party and self-hosted
- The licensing difference is a policy question, not an engineering one
- Migration to OpenTofu is verified by an empty plan; migration back is asymmetric and gets harder as you adopt divergent features
- Check your existing tooling’s support before anything else
Exercise
Section titled “Exercise”Use a disposable repository with both binaries installed. No cloud credentials.
-
Write a configuration using only common-subset features —
local_file,random_password, a variable, an output. Runterraform planandtofu plan. Compare. -
Apply with
terraform. Then runtofu planagainst the same local state. Predict: is it empty? -
Add a variable to a backend block. Run both. Predict: which one errors?
-
Add an
encryptionblock. Run both. Predict: what does Terraform say? -
List every tool in your real pipeline — linter, scanner, IDE extension, CI action. Check each one’s documentation for OpenTofu support. Predict: how many are unclear?
-
Estimate the migration for one real repository, using the OpenTofu lesson’s sequence. Write down the number of environments and where the empty-plan check would run.
-
Delete the repository.