Comparison

Terraform vs OpenTofu

Same language, same providers, two projects with different owners and different rules. This page is the decision: who is actually constrained by the licence, where the two tools have quietly diverged, and which one to run in five common situations.

In short — OpenTofu is the MPL-2.0 fork of Terraform maintained under the Linux Foundation; Terraform 1.6 and later ship under the Business Source License, which restricts offering it as a competing hosted or embedded product. For internal use both are permitted, most configurations run unchanged on either, and the real differences are governance, a few language features, S3 locking policy, and the hosted platform around Terraform.
Terraform vs OpenTofu: summary by dimension
DimensionTerraformOpenTofu
LicenceBUSL 1.1 (1.6.0+); converts to MPL 2.0 four years after each releaseMPL 2.0
StewardIBM (HashiCorp)Linux Foundation (CNCF Sandbox since April 2025), community governance
Latest release (Sep 2026)v1.16.4v1.12.6
Configuration languageHCL, .tf filesHCL, .tf plus .tofu (wins over .tf of the same name)
S3 state lockinguse_lockfile (GA since 1.11); DynamoDB locking deprecateduse_lockfile preferred; DynamoDB fully supported, no deprecation planned
Client-side state encryptionNo; rely on backend encryption at restYes: encryption block for state and plan files, KMS or passphrase keys
provider for_eachNo (alias only)Yes
Hosted platformHCP Terraform, Terraform Enterprise, StacksNone first-party; third-party platforms (the vendors behind the fork)
Published Jun 1, 2025·Updated ·By SecOpsLog

One codebase, two projects

OpenTofu began as a fork of the last MPL-licensed Terraform release line (1.5.x) after HashiCorp moved Terraform 1.6 to the Business Source License. The fork was initiated by companies that build on Terraform (Gruntwork, Spacelift, Harness, Env0, Scalr and others, per the OpenTofu FAQ) and handed to the Linux Foundation. That origin explains most of what follows: OpenTofu exists to keep an MPL-licensed engine for the vendor ecosystem, so it prioritises compatibility with Terraform configurations and the shared provider ecosystem, and it has since added features of its own (client-side state encryption, provider iteration, .tofu overrides).

The two still share the language, the plan-and-apply workflow, the state file format and the providers. OpenTofu has no providers of its own; it resolves them from the OpenTofu Registry, which mirrors the public provider and module ecosystem. What they do not share any more is ownership, release cadence, and a growing list of features on each side.

Licensing: who is actually restricted

This is the question most teams ask first and answer wrong. Read the licence text rather than the commentary. Terraform 1.6.0 and later are licensed under BUSL 1.1 with an Additional Use Grant: you may make production use of Terraform "provided Your use does not include offering the Licensed Work to third parties on a hosted or embedded basis in order to compete with" IBM's paid versions. The same text says that "hosting or using the Licensed Work(s) for internal purposes within an organization is not considered a competitive offering", and that products "not provided on a paid basis are not competitive". Each release converts to MPL 2.0 four years after it is published.

So for an engineering team running Terraform against its own cloud accounts, in its own CI, the licence changes nothing in practice. The restriction bites vendors: a paid CI/CD, platform or managed-Terraform product that wraps the binary is what the Additional Use Grant is written for. OpenTofu is plain MPL 2.0, with no use grant to interpret, which is why the platforms that started the fork ship it. That is a fact about the licence, not a judgement about the companies.

Inference, not licence advice
Whether a specific internal platform team that offers Terraform runs to other business units counts as "third parties" is a question for your counsel, not for a comparison page. The licence text says affiliates under common control count as one organization. If that sentence does not obviously cover you, the clean answer is OpenTofu, because the question then disappears.

Version context as of September 2026

Terraform is at v1.16.4 (released 23 September 2026) and OpenTofu at v1.12.6 (19 August 2026), per each project's GitHub releases page on the verification date. Terraform 1.16 is documented as "a minor release in the stable Terraform v1.0 series" that honours the v1.0 compatibility promises. OpenTofu's documentation site was publishing 1.12.x as current with 1.13 in beta. The numbers are not comparable across projects; OpenTofu continued Terraform's 1.x numbering from 1.6.0 when it forked and has shipped major features on its own schedule since.

Where the two have diverged

CapabilityTerraform (1.16)OpenTofu (1.12)Why it matters
S3 native lockinguse_lockfile, GA in 1.11; DynamoDB arguments deprecateduse_lockfile preferred; DynamoDB "fully supported", no deprecation plannedDecides whether your lock table has a deadline
State and plan encryptionBackend-side only (HCP Terraform, S3 encrypt, GCS keys)Client-side encryption block; unrecoverable without the keyThreat model for a leaked state object
Ephemeral values / write-only argsYes (1.10 / 1.11)Yes (ephemeral variables, resources, write-only attributes)Keeps secrets out of state on both
provider for_eachNo; provider block takes alias onlyYes, on aliased providersMulti-region and multi-account modules
File extension.tf.tf and .tofu (.tofu wins on a name clash)Shipping tool-specific files in shared modules
Variables in backend configNo; a backend block cannot refer to input variables, locals or data sourcesYes; variables and locals that resolve during tofu initPer-environment state location without -backend-config files or wrapper scripts
Excluding resources from a run-target only-exclude and -exclude-file, plus -targetRecovery runs that skip one broken resource
Hosted platformHCP Terraform, Terraform Enterprise, Stacks (GA)None first-party; third-party platformsWhether a vendor runs your applies

Two of those rows are the ones that change decisions: locking policy and encryption. The rest are conveniences you will notice only if you hit them.

Compatibility and the migration you would actually run

OpenTofu's own guidance is careful: it "aims to maintain compatibility with Terraform configurations" and "most Terraform code will work without modification", and the FAQ states it "will work with existing state files up to those created with Terraform versions 1.5.x". Read the second sentence literally: the promise covers state created by Terraform up to 1.5.x and says nothing about state written by 1.6 or later. Our reading, not a documented guarantee, is that a migration is safest from a state file a 1.5.x-era Terraform could still read, and that it becomes a one-way door once OpenTofu-only features (encryption, .tofu files, provider iteration) touch the code or the state.

The documented migration is short and reversible as long as you keep the backups it tells you to take: back up state and code, install OpenTofu, run tofu init, run tofu plan and expect "No changes", then tofu apply so the state is written by OpenTofu, then make one small change end to end. Rolling back means restoring the backup and running terraform init and plan again. In a CI-driven estate this is a branch, a pinned binary, and a plan you can diff.

migration dry run
cp terraform.tfstate terraform.tfstate.pre-tofu # or: rely on S3 bucket versioning
tofu init
Initializing the backend...
Initializing provider plugins...
OpenTofu has been successfully initialized!
tofu plan
No changes. Your infrastructure matches the configuration.
# anything other than "No changes" here: stop, do not apply, read the diff

The transcript above is the shape the migration guide tells you to expect, not a recording from a specific run; the exact provider lines depend on your configuration. The decision value is in the last line: OpenTofu's documented success criterion is a plan with no changes, and its documented failure response is "do not apply".

S3 state locking: the one place the advice is different

Both tools now implement native S3 locking through a .tflock object next to the state, enabled with use_lockfile = true. The policies around it differ, and this is where teams copy the wrong advice across the fence.

Terraform: the S3 backend reference marks dynamodb_table as deprecated, says DynamoDB-based locking "will be removed in a future minor version", and allows both mechanisms to be configured at once "to support migration from older versions of Terraform that only support DynamoDB-based locking". The 1.11 upgrade guide made S3 native locking generally available and deprecated the DynamoDB arguments. The lock file needs s3:GetObject, s3:PutObject and s3:DeleteObject on the .tflock key, and Terraform does not need s3:DeleteObject on the state object itself.

OpenTofu: the S3 backend reference calls native S3 locking "the preferred one" and DynamoDB "another option", and then states in a note that "both S3 and DynamoDB locking mechanisms are fully supported, and the OpenTofu team has no plans to deprecate either option". When both are configured, OpenTofu documents the semantics precisely: it acquires the S3 lock first, then the DynamoDB lock, and considers the lock held only when both succeed.

backend.tf (works in both; comments are the policy difference)
terraform {
backend "s3" {
bucket = "acme-tfstate"
key = "prod/network/terraform.tfstate"
region = "eu-west-1"
encrypt = true
use_lockfile = true
# Terraform: keep dynamodb_table ONLY during the migration window.
# The argument is deprecated and scheduled for removal in a minor release.
# OpenTofu: keeping it is a supported dual-lock (S3 first, then DynamoDB,
# lock held only when both succeed). Remove it when you no longer want it,
# not because a deprecation forces you to.
# dynamodb_table = "acme-tf-locks"
}
}

Practical consequence: on Terraform, plan to retire the DynamoDB table; on OpenTofu, the table is a choice you can keep. If a repository is used by both tools (a Terragrunt estate mid-migration, for example), write the backend for the stricter tool and note which one owns the schedule.

State encryption and secrets in state

Both tools write secrets into state in plain text unless you stop them, and both now offer ephemeral values that never reach state: Terraform since 1.10 (with write-only resource arguments in 1.11), OpenTofu with ephemeral variables, resources, outputs and write-only attributes in its 1.12 documentation. For the secrets that still land in state, the tools part ways.

Terraform's answer is the backend: HCP Terraform "automatically encrypts state at rest", the S3 backend encrypts at rest when encrypt is enabled, GCS supports customer-managed keys. The state object is therefore readable by anyone with read access to the bucket and the key. OpenTofu adds a second layer: an encryption block that encrypts state and plan files on the client before they reach any backend, keyed by a passphrase or a KMS provider (the AWS KMS example in the docs is three lines), with a fallback block for rotating keys. The docs are blunt about the cost: "when you enable encryption, your state and plan files become unrecoverable without the appropriate encryption key", and OpenTofu "will refuse to read plain text data" once encryption is on, so you must follow the documented migration steps and rehearse recovery first.

encryption.tofu (OpenTofu only; Terraform has no equivalent block)
terraform {
encryption {
key_provider "aws_kms" "state" {
kms_key_id = "a4f791e1-0d46-4c8e-b489-917e0bec05ef"
region = "eu-west-1"
key_spec = "AES_256"
}
method "aes_gcm" "state" {
keys = key_provider.aws_kms.state
}
state { method = method.aes_gcm.state }
plan { method = method.aes_gcm.state }
}
}
Encryption is a threat-model decision, not a checkbox
Client-side encryption protects against a leaked or over-shared state object. It does not protect the state from the person running the CLI, and it turns a lost key into lost infrastructure state. If your bucket policy, KMS grants and access logging are already tight, backend encryption on either tool may be enough; if state objects get copied around (laptops, artifact stores, shared buckets), OpenTofu's block is a real advantage.

Language and workflow differences that change how you write modules

provider for_each is the clearest one. OpenTofu lets an aliased provider block carry for_each, so a module can instantiate one provider per region or account from a map. Terraform's provider block, per its reference, takes alias (and a deprecated version argument) and nothing else, so the same design is done with one alias per region, written out by hand or generated. This is an inference from the two references rather than a benchmark, but it is the kind of thing that shows up as fifty near-identical provider blocks in a real multi-account repo.

The .tofu file extension is the other one worth knowing. OpenTofu loads .tf and .tofu files and, when both exist with the same base name, "will prioritize the .tofu file and ignore the .tf file". A shared module can therefore carry a Terraform-compatible file and an OpenTofu-specific override side by side, which is the mechanism for adopting OpenTofu features without breaking Terraform consumers of the module.

The day-to-day workflow is otherwise the same on both: init, plan, apply, the same providers, the same state format. The command name changes from terraform to tofu and CI images change; the muscle memory does not.

The platform around the binary

Terraform is one half of a product. HCP Terraform and Terraform Enterprise host the runs, encrypt state at rest, and are where Stacks live; the documentation describes Stacks as "a powerful configuration layer in HCP Terraform" and documents their generally available (GA) release. If those are load-bearing for you, the binary choice is made for you: Stacks and the HCP run model are Terraform features.

OpenTofu has no first-party platform. Its platform story is the third-party ecosystem that created it, plus plain CI. For a team already running plans and applies in GitLab or GitHub pipelines with an S3 or GCS backend, that is not a gap; for a team that bought HCP Terraform for its workspace model and policy sets, it is the whole decision.

Five situations, five answers

Scenario recommendations

SituationRunWhyPrimary trade-off
Existing Terraform estate, already on 1.6+, internal use only, no HCP dependencyStay on Terraform for now; keep the OpenTofu migration rehearsedThe licence does not restrict you and the estate is past the 1.5.x state line the FAQ promises compatibility for; a forced migration buys nothing todayYou inherit the DynamoDB deprecation schedule and any future licence change
Greenfield, open-source-first team, CI-driven applies, S3/GCS backendOpenTofuMPL 2.0 with no use grant to interpret, client-side state encryption, provider for_each; nothing in the HCP platform is being given upSmaller vendor support market; .tofu features are a one-way door
Regulated enterprise that wants a support contract and a hosted control planeTerraform with HCP Terraform or EnterpriseThe requirement is the platform (hosted runs, state encrypted at rest, Stacks), and that platform runs TerraformVendor lock-in is the point; budget accordingly
Team dependent on Stacks or other HCP Terraform / Terraform Enterprise featuresTerraformThose are Terraform-only features by definitionSame as above
Vendor or platform team offering IaC runs to paying customersOpenTofuThe BUSL Additional Use Grant is written for exactly this case; OpenTofu removes the questionYou own the migration and the support
Organisation evaluating migration risk but not forced to moveRun the documented dry run on one non-critical stack and keep both binaries pinnedA tofu plan showing "No changes" against a backed-up state is the cheapest evidence you can buyTwo toolchains to keep patched until you decide

The trade-offs, stated with their costs

Licence clarity. Benefit: OpenTofu removes a legal interpretation from the roadmap. Cost: you leave the project with the larger commercial support market and the first-party platform. Who cares: vendors and platform teams first, internal teams barely.

Client-side state encryption. Benefit: a leaked state object is ciphertext. Cost: key custody becomes a production dependency, and the docs warn the state is unrecoverable without the key. Who cares: teams whose state leaves the bucket, and anyone who has watched a state file end up in a build artifact.

Locking policy. Benefit on Terraform: a clear push toward one mechanism. Cost: a migration you did not schedule. Benefit on OpenTofu: no forced change. Cost: two supported mechanisms means two things to document. Who cares: anyone with a DynamoDB table and more than a handful of stacks.

Feature velocity. Benefit: OpenTofu ships community-requested features (provider iteration, client-side encryption, .tofu overrides). Cost: each one you adopt widens the gap to Terraform and narrows your exit. Benefit on Terraform: Stacks and a hosted platform. Cost: the best of it is behind HCP. Who cares: module authors who publish to both audiences, and anyone planning to keep the option to switch.

Performance. There is no first-party benchmark comparing the two, and this page ran none. Both tools execute the same providers, which is where most of the wall-clock time goes, so a performance argument for either would need measurement we do not have.

What we would run, and why

For a new platform team with a CI-driven workflow and no HCP contract: OpenTofu. The licence is unambiguous, state encryption is a feature we would actually turn on for production state, and the migration guide's rollback path means the choice is cheap to reverse early. That is a recommendation, and the evidence behind it is the licence text and the encryption reference, not a production war story.

For an estate that is already on Terraform 1.6 or later and uses only the open-source CLI internally: stay, but rehearse. The licence does not touch internal use, moving state across the 1.5.x line is outside OpenTofu's stated compatibility promise, and the concrete thing you must do on Terraform is retire DynamoDB locking on a schedule you control. Keep the migration dry run in a runbook so the decision remains yours.

For anyone who bought or needs HCP Terraform, Terraform Enterprise or Stacks: Terraform, without agonising. The engine choice follows the platform. The one thing we would not do in any of these cases is decide based on a feature table alone; run the plan against a backup and read what comes out.

Found a technical issue on this page? Report it with the tool version you used and the behavior you saw. How resources are maintained.

Go deeper
Hands-on courses on Terraform & OpenTofu