A reader sent me a question I’ve been getting more often lately: they’re running Terraform 1.5, S3 backend, no Terraform Cloud, no HCP. Is it worth moving to OpenTofu?
Short answer: probably yes, and this setup happens to be close to the easiest case there is. Here’s why.
The fork point matters
OpenTofu forked from Terraform 1.5.x, the last version released under the MPL license before HashiCorp switched to BSL. So if you’re still on 1.5, you’re migrating from the exact commit history OpenTofu started from — no state format drift, no provider compatibility surprises. tofu reads state that Terraform 1.5 wrote without any conversion step. If you’re going to make this move, doing it from 1.5 is about as clean as it gets.
What OpenTofu has that Terraform doesn’t
A few things shipped in OpenTofu and never made it into Terraform:
- State and plan encryption, since 1.7. Your state gets encrypted at rest regardless of backend, with the key coming from an env var or a KMS (AWS KMS, GCP KMS, OpenBao). Terraform has no equivalent — HashiCorp’s own docs are upfront that “anyone who can access those files can access your sensitive values,” and the fix they point you to is picking a backend that happens to encrypt at rest (S3’s
encryptoption, GCS CSEK/CMEK, HCP Terraform). That’s backend-dependent encryption, not something Terraform itself does — switch to a backend without it and you lose the protection. OpenTofu’s encryption travels with the state regardless of where it’s stored. for_eachon provider configuration blocks, since 1.9. If you’ve ever hand-written ten near-identicalprovider "aws" { alias = "region-x" }blocks for a multi-region setup, this gets rid of that.- Dynamic
prevent_destroy, since 1.12, which shipped a few months ago. You can now reference a variable insideprevent_destroy, so a module can default to protecting a database in prod but let a dev environment override it with a flag. Terraform’sprevent_destroyonly takes a literaltrueorfalse, which is why people have been working around it withterraform state rmor separate modules for years.
None of these forces a migration by itself. But they add up fast if you’ve been maintaining workarounds for any of them.
When I’d tell you to wait
There are a few real blockers:
- You’re using Terraform Stacks. OpenTofu doesn’t have an equivalent yet.
- You depend on Terraform Cloud or HCP for remote runs, Sentinel policies, or the private registry. OpenTofu doesn’t plug into any of that.
- You have a vendor contract or compliance requirement tied to HashiCorp specifically. That’s a legal question, not a technical one, and no blog post is going to settle it for you.
If none of those apply to you, they don’t apply to the setup in the question either — plain S3 backend, no TFC, no HCP.
What the migration actually looks like here
For an S3-backend setup like this, it’s mostly a non-event:
- Install
tofualongsideterraform— they coexist fine on the same machine. - Point CI at the
tofubinary instead ofterraform. - Run
tofu initagainst your existing S3 backend and state key. Nothing to change. - Run
tofu planand compare it to whatterraform planshows. It should come back empty.
The one thing worth double-checking is the provider registry: OpenTofu uses its own registry by default, but it mirrors the HashiCorp registry for almost every provider, so this rarely causes problems in practice. If you already pin provider sources explicitly (source = "hashicorp/aws"), you won’t notice a difference either way.
My take
If you’re on Terraform 1.5 with a plain S3 backend and nothing tying you to TFC or HCP, you’re the target audience for this migration — not because OpenTofu is dramatically better, but because you have nothing holding you back and a few concrete reasons to move. State encryption alone is worth it if you’ve ever had to explain to a security team why your Terraform state sits in S3 in plaintext.
If any of the blockers above do apply, don’t force it. OpenTofu is a solid project, but migrating around a dependency you can’t actually drop just leaves you with a worse version of the setup you already have.