DEV Community

Cover image for OpenTofu vs Terraform in 2026: What Actually Changed
James Joyner
James Joyner

Posted on

OpenTofu vs Terraform in 2026: What Actually Changed

I've been running both Terraform and OpenTofu across production infra for a while now, and the number one question I still get is some version of "wait, aren't they the same thing?" The honest answer in 2026 is: they share a common ancestor and a lot of DNA, but they are no longer the same tool. Here's what actually changed, from someone who has to keep both green in CI.

A very short history of the fork

If you missed the drama: HashiCorp relicensed Terraform from the MPL open-source license to the Business Source License (BSL) in 2023. The BSL is source-available but not OSI-approved open source, and it carries a use restriction aimed at competitors. A chunk of the community, backed by a group of vendors and users, forked the last MPL-licensed Terraform codebase. That fork landed under the Linux Foundation as OpenTofu.

So the core distinction is governance, not features: OpenTofu is a Linux Foundation project with open governance and an MPL-2.0 license, and Terraform is a HashiCorp product under the BSL. That licensing split is the reason a lot of teams looked at OpenTofu at all — if your legal team is nervous about the BSL's use restriction, an actual open-source license is the whole ballgame.

The CLI and state are (mostly) drop-in compatible

The thing that makes OpenTofu practical to adopt is that it started as a literal fork. The binary is tofu instead of terraform, and for a lot of everyday work it behaves identically:

tofu init
tofu plan -out plan.tfout
tofu apply plan.tfout
tofu state list
Enter fullscreen mode Exit fullscreen mode

It reads your existing .tf files, understands the same HCL, and uses the same state file format. On real infra I've pointed tofu at a state file that was last touched by Terraform and had it produce a clean, no-change plan. That parity is not an accident — keeping the state format compatible is what makes migration a low-drama exercise rather than a rewrite.

But "mostly compatible" is doing real work in that sentence. The two projects have been diverging since the fork, and the gap widens with every release. Treating them as interchangeable is where teams get burned, so let's talk about the divergences that actually matter.

The real divergences in 2026

State and plan encryption, natively

This is the feature I care about most. OpenTofu ships native state and plan encryption. You configure it directly in your OpenTofu configuration, pick a key provider (PBKDF2 with a passphrase, a cloud KMS, and so on) and a method, and OpenTofu encrypts state at rest — including the plan file, which can leak secrets just as badly as state.

terraform {
  encryption {
    key_provider "pbkdf2" "mykey" {
      passphrase = var.encryption_passphrase
    }

    method "aes_gcm" "default" {
      keys = key_provider.pbkdf2.mykey
    }

    state {
      method = method.aes_gcm.default
    }

    plan {
      method = method.aes_gcm.default
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

In the Terraform world you traditionally solved this at the backend level — a KMS-encrypted S3 bucket, restrictive IAM, and hoping nobody terraform shows a plan file into a CI log. OpenTofu moves encryption into the tool itself. If you handle regulated data, this alone can justify the switch.

Early variable evaluation

For years the answer to "why can't I use a variable in my backend block?" was a shrug. OpenTofu added early variable evaluation, which lets you use variables (and locals) in places that used to demand static literals — most notably backend configuration and module sources. That means you can drive your backend bucket or key by variable instead of maintaining a wall of -backend-config flags or partial-backend hacks.

It's genuinely useful, and it's also a one-way door: a config that relies on early eval in a backend block won't parse cleanly under stock Terraform. Keep that in mind before you sprinkle it everywhere.

Provider-defined functions

Both ecosystems moved toward letting providers ship their own functions rather than waiting for the core team to add every string-munging helper. In OpenTofu you call them through the provider:: namespace:

locals {
  parsed = provider::aws::arn_parse(var.role_arn)
}
Enter fullscreen mode Exit fullscreen mode

The exact set of available functions depends on the provider version you're pinning, so I won't quote a catalog — check the current provider docs. The point is that the language surface is no longer frozen to whatever core ships.

.tofu and .tofu.json override files

This is a small feature with big ergonomic payoff. OpenTofu recognizes .tofu and .tofu.json files, and it prefers them over the equivalent .tf/.tf.json when both exist. That gives you a clean way to keep a shared codebase that runs under both tools: keep the common config in .tf, and drop OpenTofu-specific overrides in .tofu files that Terraform simply ignores.

main.tf          # shared, runs under both
backend.tofu     # OpenTofu-only overrides, invisible to terraform
Enter fullscreen mode Exit fullscreen mode

If you're maintaining a module that needs to support both tools during a transition, this is the mechanism that keeps you sane.

The registry

OpenTofu runs its own provider and module registry rather than depending on HashiCorp's. In practice most of the popular providers are mirrored and resolve fine, but the source of truth is different, and provider/module availability is something to actually verify rather than assume. If you have a niche or internal provider, confirm it publishes where OpenTofu looks for it.

A note on version numbers

I'm deliberately not quoting exact version numbers or "OpenTofu is X% faster" benchmarks, because those age badly and half the ones you'll see online are made up. As of 2026 both projects are shipping regularly and the feature sets keep moving — treat any specific version claim (including mine) as something to verify against the current docs before you build a decision on it.

So which do you pick, and should you switch?

Here's my honest take.

If you're starting green today, I'd default to OpenTofu. You get a real open-source license, native state/plan encryption, and the divergent features are mostly additive quality-of-life wins. The compatibility story means you lose almost nothing by choosing it.

If you're an existing Terraform shop, the calculus is about your actual pain. Switch if the BSL license is a genuine legal or procurement problem, or if native state encryption solves a compliance requirement you're currently duct-taping. Don't switch just to be on the trendy side of a fork — a migration is still real work and real risk, even when it's low-risk.

If you depend on Terraform Cloud / HCP-specific features or a paid workflow tightly coupled to HashiCorp's platform, weigh that integration honestly. OpenTofu is the engine, not the whole platform, and you'll be assembling backend, state, and workflow pieces yourself or via third-party platforms.

Whatever you choose, the one thing I'd avoid is drifting into using divergent features by accident and then being surprised you can't go back. If you want a cross-referenced dive into the specific errors and edge cases I hit while running OpenTofu on real clusters, I keep my OpenTofu troubleshooting guides updated as I trip over new ones.

Takeaway

OpenTofu and Terraform are close cousins, not twins. The compatibility is real enough that adoption is cheap, but the divergences — state/plan encryption, early evaluation, provider-defined functions, .tofu overrides, and a separate registry — are real enough that you should choose deliberately and know which one-way doors you're walking through. Pick based on your license posture and your compliance needs, pin your versions, and read the current docs before betting on any specific feature.

Top comments (0)