OpenTofu vs Terraform: The Fork One Year In
In August 2023, HashiCorp changed Terraform’s license from MPL 2.0 to BSL 1.1 (Business Source License). By January 2024, the Linux Foundation had adopted a fork called OpenTofu. Eighteen months later we are living with the consequences: two tools that share a heritage and most of their DNA, diverging slowly, with a community of users trying to decide which one they actually want to be on for the next decade. This post is the view from where we are now — what the licensing change meant, what actually differs between the tools, where they have diverged technically, what the migration path looks like, and how the decision plays out for teams of different sizes and postures.
I’m going to try to be fair to both sides. HashiCorp did not abandon Terraform and it’s not secretly dying; the BSL doesn’t suddenly make it unusable for 99% of teams. OpenTofu is not a rogue renaming exercise; it has real engineering behind it and real features Terraform does not have. Most teams will be fine picking either one, and for most, the choice has more to do with existing relationships and team comfort than with any technical capability.
What the license change actually did
The MPL 2.0 is permissive — you can use the software commercially, fork it, and do nearly anything provided you preserve the license. The BSL 1.1, as HashiCorp adopted it, says you can use the software freely except to build a competing product or service. After four years, each release’s license converts to MPL 2.0 — so in 2028, Terraform 1.5 (the last MPL version) will finally also apply to Terraform 1.10’s code, which will itself convert in 2029, and so on.
The net effect:
- End users (people writing Terraform to manage their infrastructure) — mostly unaffected. HashiCorp made clear the change isn’t targeting you. Running
terraform applyin your CI pipeline, in a consulting engagement, in a training course — all still permitted. - Platform vendors (Spacelift, env0, Scalr, Terraform Cloud competitors) — directly affected. The BSL’s “non-compete” clause is ambiguous enough that every vendor had to consult lawyers about what they could and couldn’t do.
- Open-source ecosystem — chilled. When a project has BSL status, downstream tools inherit uncertainty.
That third effect drove the fork. A consortium of vendors (Harness, Gruntwork, Spacelift, env0, and others) plus community contributors put their money and engineering behind OpenTofu, which is MPL-licensed, Linux Foundation-governed, and forked from Terraform 1.5.6 (the last MPL release).
So: Terraform stays under HashiCorp’s (now IBM’s, after the 2024 acquisition) control, with BSL license. OpenTofu is a vendor-and-community project under the Linux Foundation, MPL-licensed. Both are “open source” in the colloquial sense — source available, freely usable — but “open source” in the OSI sense only applies to OpenTofu.
Compatibility: closer than you’d expect
OpenTofu’s first release was effectively a rename of Terraform 1.5.6 with a cleaner module registry ecosystem. The two have been diverging since. As of early 2026:
- HCL syntax — identical in both. Your
.tffiles work in either tool. - Provider protocol — identical. OpenTofu can use any Terraform provider; Terraform can use any OpenTofu provider. The provider registry for each is separate but the HCL side doesn’t care.
- State file format — same schema on disk. OpenTofu will read Terraform state and vice versa. Some newer features add state fields that the other tool ignores.
- CLI commands — same names (
plan,apply,destroy,import,state,workspace, etc.).tofu applyandterraform applybehave effectively identically on simple configs. - Backends — same backends (S3, GCS, Azure blob, HTTP, Postgres, local). Compatible.
If you have a Terraform 1.5 codebase, brew install opentofu and run tofu init && tofu plan — the overwhelming majority of the time, it works without modification. The remote state you’ve been writing from Terraform will be read by OpenTofu without conflict.
Where they have diverged
This is where the “one year in” assessment matters. The tools have been drifting. In roughly chronological order:
State encryption (OpenTofu 1.7)
OpenTofu shipped client-side state encryption as a first-class feature. You declare in the config:
|
|
And the state file is encrypted before hitting the backend. Key providers support PBKDF2, AWS KMS, GCP KMS, or OpenBao/Vault. This is a real gap in Terraform — if you’re storing state in S3, you’re depending on S3 bucket policy and KMS-at-rest; there’s no client-side encryption.
Terraform has not implemented this feature and HashiCorp’s stated view is that state encryption belongs in the backend, not in the client.
-exclude flag and removed-block improvements (OpenTofu 1.7-1.8)
OpenTofu added tofu plan -exclude=<resource> to plan everything except specified resources. Useful when you want to touch everything but one stubborn thing. Terraform has -target for the inverse; OpenTofu has both.
OpenTofu also added removed blocks for module-level removal declarations, generalizing what Terraform introduced at resource level.
Dynamic provider aliases and providers in modules (OpenTofu 1.8)
OpenTofu eased the restriction that providers in modules must be known at plan-time. A common complaint with Terraform is “I can’t for_each over a module that needs per-instance providers” (e.g., one AWS provider per region). OpenTofu relaxed this, Terraform has not yet.
Early evaluation / static variables (OpenTofu 1.8)
OpenTofu added early evaluation — some variables and locals can be evaluated before the dependency graph is built, which lets you do things like pull module sources from variables:
|
|
Terraform still requires module sources to be static strings. This one feature is genuinely useful at scale and has been a community request for years.
Provider iteration / for_each on providers (OpenTofu 1.9)
OpenTofu added provider iteration:
|
|
And then modules can consume these dynamically. Terraform can’t do this — you have to hand-declare each aliased provider.
Stacks (Terraform only)
On the Terraform side, the big 2024 feature was Stacks — a higher-level abstraction for managing multi-environment deployments, introduced in preview through Terraform Cloud/Enterprise. Stacks let you define a template once and deploy it to many targets with variables bundled per-target. This is genuinely useful and OpenTofu does not have an equivalent.
However, Stacks in practice is tightly coupled to HashiCorp’s cloud offering. The CLI-only experience of Stacks is more limited; the full power is in Terraform Cloud/HCP Terraform.
Stacks (OpenTofu, 1.10-ish)
OpenTofu has announced and begun implementing its own multi-environment model, referred to informally as “OpenTofu Stacks” or “environments,” but it’s a different design, targeting the OSS and self-hosted case rather than a cloud service. As of this writing the feature is partial.
Registry governance
Terraform’s public registry (registry.terraform.io) is HashiCorp’s. OpenTofu maintains its own registry mirror. Most providers (AWS, Azure, GCP, Kubernetes, Helm, etc.) publish to both; community providers might be on one or the other.
OpenTofu’s registry has a transparent, community-governed publishing process — any project can submit. Terraform’s is HashiCorp-operated. Practically, both have the providers you care about for mainstream use.
Migration: what it takes
For a typical Terraform codebase, migration to OpenTofu is usually:
|
|
Most codebases show zero diff. The exceptions tend to be:
- Remote state from Terraform Cloud/HCP — Terraform Cloud as a backend works with OpenTofu (HCP hasn’t blocked it, though they could) but the “remote runs” feature — where the apply happens on HashiCorp’s infrastructure — is Terraform-only. If you depend on HCP for execution, you’d need to switch to self-hosted or a third-party platform (Spacelift, env0, Scalr, Terrakube).
- HCP-specific data sources — a handful of provider features that only exist in paid HashiCorp products may not work the same.
- Terraform Stacks — if you’re using Stacks, there’s no OpenTofu equivalent yet.
- Modules fetching from
registry.terraform.io— OpenTofu resolves to its own registry by default; can be configured to use Terraform’s.
For state: the state file is mutually readable. You can run OpenTofu against Terraform’s state, it’ll add a small marker field, and Terraform will read it back (with a warning about the marker but no functional issue). Going back and forth works.
The gotchas
A few real things that have bitten teams:
- Mixed CI. If half the team runs
terraformand half runstofu, state file metadata fields flip-flop on each apply. Pick one. - Provider version pinning. Terraform and OpenTofu pull the same provider but from different registries; pinning to a specific version gives identical behavior. Using
version = "~> 5.0"can resolve differently if one registry has an out-of-order release. Pin specifically in production. terraformkeyword blocks. OpenTofu honors theterraform { }block name for compatibility. It also acceptstofu { }. Mixing them in the same file is syntactically legal but confusing.TF_*environment variables. OpenTofu honorsTF_*env vars for compatibility, but also acceptsTOFU_*. Your CI might set both; make sure they agree.- Lock files.
.terraform.lock.hclvs.terraform.lock.hcl(OpenTofu uses the same filename) — but the contents differ slightly when resolved from different registries. Commit one tool’s lockfile and stick with it.
None of these are dealbreakers. They’re “read the docs and migrate with intention” issues, not “the tool is broken” issues.
What HCP Terraform / Terraform Cloud gets you
Being clear about the paid tier matters, because it’s where the HashiCorp story really differs from OSS-OpenTofu:
- Remote runs (apply executes on HashiCorp’s infrastructure, not your laptop)
- VCS integration (auto-plan on PR, auto-apply on merge)
- State versioning, locking, drift detection in a managed UI
- Policy-as-code (Sentinel, and OPA support)
- RBAC / SSO / audit logs
- Terraform Stacks (enterprise-tier)
- Cost estimation
- Private module registry
- Private provider registry
For teams on HCP Terraform’s paid plan, the value is in the managed runs and policy integration, not in the OSS CLI. Migrating to OpenTofu means replacing the CLI with tofu but you also lose the managed run platform unless you bring your own (Spacelift, env0, Scalr, Atlantis, Terrakube, or building one on Nomad/K8s yourself).
This is the real decision point for many teams. It’s not “Terraform vs OpenTofu” — it’s “do I want to be on HCP for the managed experience, or do I want to self-host the execution plane on OpenTofu?”
Where the communities are going
My read of activity over the past year:
- OpenTofu’s development velocity is high. Releases every 4-6 months with substantive new features. The maintainers are doing the work of catching up and then pulling ahead on OSS-friendly features.
- Terraform’s velocity is focused on the commercial product. The OSS CLI gets bug fixes and modest improvements; the interesting new work is in HCP Terraform features. This is rational for HashiCorp — that’s where the revenue is — but it means the OSS CLI is no longer the flagship.
- Providers are mostly neutral. AWS, Azure, GCP providers are still HashiCorp-maintained but the protocol allows OpenTofu to use them. Community providers are now published to both registries routinely.
- The Linux Foundation umbrella is giving OpenTofu a governance story that insulates it from any single vendor’s strategy change. Users who got burned by the BSL shift are migrating partly for the license and partly for the governance.
- Third-party IaC platforms are hedging. Spacelift, env0, Scalr all support both. New products (Digger, Terrakube, OpenTofu’s upcoming cloud) are OpenTofu-first.
How to decide, practically
If I were joining a new company tomorrow and they asked me to pick:
Go with OpenTofu if:
- You’re OSS-first and the license matters to you (auditors, public sector, open-source ecosystem contributors).
- You like the recent OpenTofu features (state encryption, dynamic providers, early variables) and want to use them.
- You’re self-hosting your execution plane anyway (Atlantis, GitLab CI, GitHub Actions, a runner on your K8s cluster).
- You value Linux Foundation governance and transparent roadmaps.
- You’re building tooling that wraps or extends the IaC tool, and the BSL’s “non-compete” ambiguity is a concern.
Stay with Terraform (or pick it fresh) if:
- You’re deeply invested in HCP Terraform for runs, policy, and RBAC and it’s working.
- Terraform Stacks is on your roadmap.
- Your team is highly familiar with Terraform and you don’t need the new OpenTofu features.
- You have vendor support contracts that matter (enterprise licenses, paid support).
- You’re in an environment where “HashiCorp” is a known-quantity vendor with a recognized procurement relationship.
For most teams, the choice is not life-or-death. Pick one, commit to it, and move on. Don’t run both. Don’t let half the team use one CLI and half the other. The downside of having two nearly-identical tools is a subtle consistency tax; the upside of committing to one is that you can use its features without worrying about cross-compatibility.
What I’ve learned from actually migrating
Having watched a couple of shops do the migration and done one myself, a few things stand out:
- The technical migration is usually a day’s work for a moderately-sized codebase. The decision-making around it takes longer than the work.
- CI is where you hit friction. Self-hosted runners invoking
terraformeverywhere need updating. Lock files diverge if you’re careless. - Provider lock file hygiene matters more post-migration. Pin specifically. Use
-upgradedeliberately. - Communicate the decision. Tell everyone who touches the repo, including contractors and partners. Having an engineer submit a PR full of
.terraform.lock.hcldiffs from their laptop because they ran the wrong binary is a preventable headache. - Test module upgrades separately. When you bump to OpenTofu and then also want to use an OpenTofu 1.8 feature, do those as separate changes so you can bisect problems.
- Don’t forget IDE plugins. The Terraform VS Code extension works with both; the tflint/terragrunt/atlantis tooling mostly does too, but verify your specific chain.
Terragrunt, Atlantis, and friends
A few tools in the broader IaC ecosystem worth noting on this split:
- Terragrunt (Gruntwork) — officially supports both;
terragrunt.hclworks identically with either CLI. Default has shifted to OpenTofu-first in its docs. - Atlantis — supports both. Config flag picks the binary.
- terraform-docs — tool-neutral (reads HCL, doesn’t invoke the CLI).
- tflint / checkov / tfsec / trivy — tool-neutral for HCL validation.
- Spacelift / env0 / Scalr — platforms that support both.
- Terrakube / Digger — OpenTofu-focused newer platforms.
The ecosystem has adapted. Nothing you depend on is exclusively Terraform or exclusively OpenTofu unless you’re on HCP.
The bigger picture
What the fork really represents is a test of how much community ownership matters in infrastructure tooling. Terraform was open source by choice for a long time; when the business pressure of competing managed offerings got high enough, HashiCorp retained the right to pull that openness back. OpenTofu exists because a coalition of companies and individuals decided that the governance of a foundational tool mattered more than the continuity of the implementation they were using.
You can take that as a victory for open source, as a cautionary tale about vendor lock-in, or as proof that forks only work when there are commercial backers willing to fund them. It’s probably some of each.
Eighteen months in, the practical reality is: both tools work, both are under active development, both will likely still exist in five years, and the day-to-day experience of using either is nearly identical. If you have no strong preference, OpenTofu is my default recommendation today — the license, the governance, the recent feature pace — but that’s weighted toward teams who self-operate. If you’re paying HashiCorp for a managed product and getting value from it, there’s no urgency to move.
The choice will matter less over time. The HCL language is likely to remain compatible for years. Providers are mostly neutral. State is exchangeable. What differs most is the business model around execution, governance, and paid features — and those are decisions about your vendor relationships, not your code.
Comments