OpenTofu vs Terraform in 2026, and how to migrate
Terraform and OpenTofu both turn configuration into cloud infrastructure. In 2026 the question is which one you should choose.
If you are a startup choosing for the first time, changing your mind later means a migration. If you pick Terraform today, you will most likely be on the latest release, and that narrows where you can run it. Some third-party platforms stop at 1.5.7, the last release under the old license, which can make HCP Terraform the main option that runs current Terraform for you. Its free tier covers up to 500 managed resources, and above that you pay per managed resource, so the cost climbs quickly as your infrastructure grows. We recommend that new users start with OpenTofu. The reason is ecosystem fit and open governance, not a gap in what Terraform can build.
If you froze Terraform at 1.5.7 or 1.6, you are running a release that no longer gets security fixes. If you kept upgrading, OpenTofu publishes a migration guide for every Terraform line up to 1.9. The move from 1.5.7 needs no code changes. The guides for 1.6 through 1.9 list a short set of known differences, mostly S3 backend options, removed blocks, test overrides and a few functions, and each one comes with a fix.
If you are on a currently supported Terraform release, which today means 1.15 or 1.16, the documented path runs out and the two products have started to drift. The action block and terraform query, added in Terraform 1.14, are in those releases, and as of October 2026 OpenTofu has no equivalent. We expect more of this, because OpenTofu cannot copy newer Terraform code and each side now builds its own features. The further the forks move apart, the more risk you carry and the more work a later migration takes. A stack that uses these features has more to unwind. A stack that does not can still move, but no guide covers it.
How did we get here?
On August 10, 2023, HashiCorp announced that future Terraform releases would move to the Business Source License 1.1. That license is source-available, not open source by the OSI definition. Competing vendors would no longer be able to use those releases in competing offerings, as HashiCorp defined them, while APIs, SDKs and most libraries stayed under MPL 2.0. Terraform 1.5.7 is the last MPL release and the BSL begins at 1.6.0. Each BSL release converts to MPL 2.0 four years after publication, so 1.6.0 converts around October 2027.
Within weeks a community group published a manifesto (opens in a new tab) and forked the last MPL commit. The public OpenTofu repository went live on September 5, 2023, the Linux Foundation announced it would host the project on September 20, and OpenTofu 1.6 reached general availability on January 10, 2024. In April 2024 HashiCorp sent a cease-and-desist letter, which OpenTofu's counsel disputed, and no public outcome has been reported. IBM completed its acquisition of HashiCorp in February 2025 and the BSL stayed in place.
The restriction fell on vendors hosting Terraform, not on a team using it for their own cloud. A company could keep upgrading. Hosted platforms that compete with HCP Terraform could not ship those releases, so they stopped at 1.5.7.
What changed
The project has shipped steadily. OpenTofu has released seven minor versions since 1.6, most recently 1.13 in September 2026, with security support through August 2027. The CNCF accepted it into the Sandbox (opens in a new tab) on April 23, 2025. Sandbox is the entry maturity level, so it has not reached Incubating.
It also has people behind it. Companies pledged funded engineering staff for at least five years starting in 2023, and the supporters page (opens in a new tab) now lists 163 companies, 12 projects, one foundation and about 791 individuals. The project is governed by a technical steering committee and hosted by the Linux Foundation and the CNCF, so no single vendor owns it.
How OpenTofu and Terraform differ in 2026
Day-to-day HCL, providers, modules, backends and the core CLI are the same, and providers did not change license. The differences are in newer features, and matching version numbers do not mean matching behavior, because OpenTofu 1.11 and Terraform 1.11 are different things. The table lists the features that matter most as of October 2026, ordered by who shipped first.
| Capability | OpenTofu | Terraform | Shipped first |
|---|---|---|---|
| Client-side state and plan encryption | 1.7 | No equivalent | OpenTofu |
| Provider-defined functions | 1.7 | 1.8 | OpenTofu |
| Variables in backend and encryption config | 1.8 | No equivalent | OpenTofu |
Variables and locals in module source |
1.8 | 1.15 | OpenTofu |
Provider for_each |
1.9 | No equivalent | OpenTofu |
-exclude flag |
1.9 | No equivalent | OpenTofu |
| OCI registry sourcing for modules and providers | 1.10 | No equivalent | OpenTofu |
enabled argument on resources and modules |
1.11 | No equivalent | OpenTofu |
language block |
1.12 | No equivalent | OpenTofu |
| Ephemeral resources | 1.11 | 1.10 | Terraform |
| Write-only attributes | 1.11 | 1.11 | Same release number |
terraform stacks (HCP Terraform only) |
No equivalent | 1.13 | Terraform |
action blocks |
No equivalent | 1.14 | Terraform |
List resources and terraform query |
No equivalent | 1.14 | Terraform |
OpenTofu shipped most of the language features first. Terraform shipped ephemeral resources a release earlier and now leads on actions and queries. Both tools now have a const argument on input variables, but the documented behavior differs. OpenTofu infers whether a variable is constant when you leave const unset, and const = false opts out. Terraform's documentation says const must be set before a variable can be used in module and provider source and version attributes. Setting const = true explicitly is the form both tools document.
terraform stacks and Sentinel belong to HCP Terraform and Terraform Enterprise, so teams outside those products can ignore them. The action block and terraform query are core language and CLI features, so teams that never touch HCP will face them. If your delivery model is built around HCP Terraform, that is a legitimate reason to stay.
Which migration guide matches your Terraform version
If you run Terraform 1.5 through 1.9, OpenTofu publishes a guide (opens in a new tab) for your exact line, and 1.5.x is the easiest case. Each guide is exact to one patch release, so you may need to move to that patch first. The guides target older OpenTofu releases, so finish by upgrading to a supported release in a separate change.
If you kept upgrading past 1.9, there is no official guide for your version. OpenTofu publishes a general migration guide, a page on interdependent configurations, and an FAQ saying state files work up to those created with Terraform 1.5.x. It does not say how OpenTofu handles state written by newer versions, so the validation is yours. Upgrade carefully and start with a trial run on a copy of your state. The next section covers how.
How to migrate from Terraform to OpenTofu
Migrate one stack at a time, starting with a low-stakes one. Each stack goes through the same four steps.
Prepare. Check what you are about to migrate. Note each stack and workspace, since each has its own state, plus the providers and modules they use and any
required_versionconstraint, which can blocktofu. Apply any pending changes and confirmterraform planshows nothing. Make sure you can roll state back, either by turning on versioning for the state bucket or by saving a copy withterraform state pull.terraform plan # expect: No changes terraform state pull > backup-pre-cutover.tfstateRun a trial on a copy. Copy the backup to a trial state file.
cp backup-pre-cutover.tfstate trial.tfstateThen comment out the real
backendblock and add a local backend that points at the copy. This is a code change, not a command, and it keeps the trial away from the shared state and its lock.terraform { # backend "<your backend type>" { ... } backend "local" { path = "trial.tfstate" } }Run
tofu init -reconfigure. Because you swapped backends,initwill not continue without a flag, and-reconfigurepoints at the new backend without copying any state, so the trial uses the file you just made. Skip-upgrade, which also moves provider versions and clouds the diff. Expectinitto rewrite the lock file, switching each provider fromregistry.terraform.iotoregistry.opentofu.org. It keeps the versions but not the hashes, because OpenTofu's provider releases are not byte-for-byte identical to HashiCorp's. Finish withtofu plan. A plan that reports no changes means the trial passed. Any diff is a reason to stop and investigate.tofu init -reconfigure tofu plan # expect: No changesCut over. This is the point of no return. Up to here OpenTofu has only worked on a copy, and the shared state has not been written to. The cutover is the first time OpenTofu writes to it, and the stack becomes OpenTofu-owned. Start by undoing the trial. Un-comment the original
backendblock and delete the local one, so the configuration is back to what it was.terraform { backend "<your backend type>" { ... } }Then point back at the real backend with
-reconfigure. Never use-migrate-statehere, because it could offer to copy the trial state over the real one. The commitment point is the first command that writes state. Apply is the intended one, because even with no changes it lets OpenTofu update the state file format if needed. Other commands that write state, includingstate mv,state rm,state pushandimport, make the stack OpenTofu-owned the same way. A saved plan keeps what you reviewed identical to what you apply. Commit the rewritten lock file with the cutover.tofu init -reconfigure tofu plan -out=cutover.tfplan # expect: No changes tofu apply cutover.tfplan # only after a clean planFollow with a small change. Make a small, non-critical change under OpenTofu. Then run read-only plans from both engines against the same commit for a couple of weeks and diff the results. That is the best canary. A clean plan in one workspace does not prove the others, so repeat steps 2 and 3 for each workspace.
Update your tooling as you go. Switch one pipeline at a time, lowest stakes first, and keep a disabled fallback job. GitHub Actions has the community-maintained opentofu/setup-opentofu action. For other tools, look for an engine or runtime setting.
After you migrate
The lock file now lists registry.opentofu.org providers. The versions carry over and the hashes are new.
provider "registry.opentofu.org/hashicorp/time" {
version = "0.14.2"
constraints = ">= 0.14.2"
hashes = [
"h1:0lkmuDlyUBEK2vAxb9r8jY8kMpqYbMoSb4GWnSbA9iY=",
"h1:4ccOXW03+ENKJieXGwTfMvRlkpT9o+ra6dw240C1UFE=",
# ...
]
}The init output still prints the short name, hashicorp/time. That is normal.
Can you switch back from OpenTofu to Terraform?
Going back is harder than going in, and it gets harder the longer you stay. OpenTofu's documentation calls the process reversible, and right after cutover, with the same features and no real changes, it is. Every apply, every new resource and every OpenTofu-only feature moves you further from that point. OpenTofu's migration pages cover one direction only, so your mileage will vary.
Rollback has two cases.
- No real change has been applied since cutover. Stop using OpenTofu, restore the state backup, run
terraform initandplan, confirm nothing unexpected, and test a small change. - Changes have been applied under OpenTofu. Do not restore the backup. The backup is older than reality, so new resources become unmanaged and the next Terraform apply may try to recreate or destroy things. Inspect the current state before you point Terraform at it, and be ready to repair it by hand. OpenTofu records its own version in the state file after an apply, for example 1.13, so the state no longer looks like Terraform wrote it. Define rollback triggers up front, such as a destroy or replace on a resource that is unchanged in code, or an unclean plan on consecutive runs.
Several things make the door one-way in practice.
- State and plan encryption. The documentation does not say Terraform can read OpenTofu-encrypted state, so plan as though it cannot. Encryption (opens in a new tab) can be unwound within OpenTofu by adding an
unencryptedmethod as primary, keeping the old method asfallback, turning offenforced, applying, and then removing the block. That does not help you return to Terraform. - Language additions. Provider
for_each, theenabledargument, thelanguageblock and variables in backend blocks have no Terraform equivalent. Terraform has its ownconst, and provider-defined functions work in Terraform from 1.8, so those are not one-way items. .tofufiles. These are a different hazard. Ifversions.tfandversions.tofuboth exist, OpenTofu loads only the.tofufile, while Terraform ignores.tofufiles. After a rollback Terraform silently runs the older.tfcontent. Avoid adding.tofufiles until a return to Terraform is no longer a goal.- Pipeline features. The
-excludeflag is a CLI flag, so it costs you a pipeline feature on rollback and nothing more. Keep Terraform installed and the old pipeline definitions on a branch for one full apply cycle, in case you need to roll back. Then remove both, so muscle memory does not send someone to runterraformagainst a stack that OpenTofu now owns. Adopt OpenTofu-only features deliberately, not during the migration window. In particular, do not turn on state encryption during migration, because it stacks a second risky change onto an already risky one.
What breaks during a Terraform to OpenTofu migration
required_versionconstraints. OpenTofu changed how it reads these. Through 1.11 it compared anyrequired_versionagainst its own version number, so a constraint such as~> 1.5.0or< 1.6rejectedtofu. From 1.12, OpenTofu's settings documentation says arequired_versionin a plain.tffile is ignored, and constraints are enforced only in.tofufiles or in alanguageblock withcompatible_with. Check which OpenTofu release you pin, search every root and shared module, and decide a policy before cutover.- Accidental Terraform runs. Reading Terraform's source code, a stray
terraform planon a migrated stack should stop at the lock file, which now listsregistry.opentofu.orgproviders, and report an inconsistent dependency lock file. Do not count on the state to protect you. Current Terraform no longer compares theterraform_versionrecorded in state with its own version, so a state last written by OpenTofu is accepted. A plan still takes the state lock, so it can block a real run.terraform initrewrites the lock file and removes that protection, andterraform applyrewrites state with Terraform's version and provider addresses. Add a CI check on the engine in use, and tell people not to runterraform initon a migrated stack. - One engine owns a stack at a time. Freeze merges to the stack during its cutover window and gate the old pipeline, so a Terraform job cannot fire against state OpenTofu just wrote.
- Registry addresses and missing providers. Fully qualified
registry.terraform.io/...sources should become short forms, for providers and modules alike. Niche, vendor-specific and private providers and modules may be absent from the OpenTofu registry. Symptoms include failures to query available provider packages and module-not-found errors. Confirm the code works on Terraform, file a registry request, or use an explicit registry.terraform.io source. - Lock file churn. Hashes and source addresses change. Use
tofu providers lockwith a-platform=flag for each platform your developers and CI use, and commit the lock file with the engine swap. OpenTofu 1.12 records checksums for all supported platforms oninit. - The
cloudblock and HCP Terraform. Since OpenTofu 1.6 thecloudandremotebackends no longer default to app.terraform.io, so sethostname(orTF_CLOUD_HOSTNAME). OpenTofu's documentation does not promise compatibility with HCP Terraform, Terraform Enterprise or Sentinel, so those teams need a different plan, and policy as code moves to OPA or platform tooling. - Hardcoded binaries, tooling and policies. Dockerfiles, scripts, linters, scanners and policy engines often call
terraformor assume.tffiles. Search for it and test each tool againsttofuand.tofu. In a mixed fleet, policies keyed to "version 1.x" become ambiguous, so log the engine too. - Remote state consumers. OpenTofu's page on interdependent configurations says to migrate consumers before the stacks they read from, so producers go last. OpenTofu reads Terraform state, but Terraform may not reliably read OpenTofu state. If you enable encryption later, consumers also need matching
remote_state_data_sourcessettings, so configure encryption keys and those settings before dependent stacks run.
Where to go from here
The right choice depends on your team. If Terraform fits how you deliver today, because of HCP Terraform, procurement rules or a vendor support contract, staying is a sound decision. Just keep in mind that the gap between the two tools widens with every release, so this is a good time to look at your Terraform strategy on purpose and not by default.
If you want to see where you stand, pick one low-stakes workspace and run the trial from step 2. It touches nothing shared, and the plan tells you how much of this post applies to you. If something blocks you, such as a missing provider, a pipeline tool that cannot run against OpenTofu, or a diff you cannot explain, you will have found it before committing anything. If the plan comes back clean, you are well on your way.
We hope this helps you make the call that fits your team. Whichever engine you choose, the next challenge is keeping infrastructure and application changes in step, which is the problem we built Admiral to solve. Admiral is an orchestrator. It brings infrastructure and workload changes into one workflow, and you keep your own tools. The work itself runs on agents in your own environment, a deliberate part of our security approach.