DEV Community

Oleksandr Kuryzhev
Oleksandr Kuryzhev

Posted on Originally published at kuryzhev.cloud

OpenTofu or Terraform: What to Pick for Existing Code

Originally published on kuryzhev.cloud


The question of OpenTofu or Terraform tends to arrive on a quiet Tuesday. A repo with 200 resources, a remote state in S3 and a CI pipeline pinned to terraform works fine, until someone asks whether the next upgrade should be to the other tool. Nothing is broken, so the real question is whether switching buys anything that justifies the risk.

The timing is worth noting. Both projects shipped new minor cycles recently: OpenTofu 1.13 (latest patch 1.13.5, cycle released 2026-09-30, release notes) and Terraform 1.17 (latest patch 1.17.1, cycle released 2026-10-21, release notes). Anyone planning a quarterly upgrade has two fresh targets. The two projects follow independent roadmaps, so check each changelog for features that exist in only one of them.

Short answer: if your code is plain AWS/GCP/Azure/Kubernetes resources and you have no contract with HCP Terraform, you can usually stay on Terraform 1.17 or move to OpenTofu 1.13 at low risk, so choose based on licensing and features. If you depend on HCP Terraform, Stacks or other HashiCorp-hosted services, stay on Terraform. If you want client-side state encryption or an open-governance licence, trial OpenTofu with the dry-run below before committing.

When the OpenTofu or Terraform choice actually matters

For a small repo with a single backend, the choice is mostly cosmetic. It starts to matter in four situations:

  • Licensing review. Terraform releases after 1.5.x use the Business Source License; OpenTofu is MPL-2.0 under the Linux Foundation. Legal teams sometimes care, particularly if you ship tooling to customers. Read the current licence text yourself rather than relying on a blog summary.
  • State sensitivity. State holds secrets in plain text unless the backend encrypts it. OpenTofu has built-in state and plan encryption. Terraform relies on backend encryption and on its ephemeral and write-only mechanisms to keep values out of state. Check the Terraform docs for what your version supports.
  • Platform coupling. If your org runs workspaces, policies and run tasks in HCP Terraform or Terraform Enterprise, the tool and the platform are one decision.
  • Feature divergence. Since the 2023 fork the two projects have added different features and, in some cases, different syntax for similar ideas. Config that uses a tool-specific feature will not run on the other.

Neither tool has a blanket advantage. Compare the changelogs for your specific version jump, not for the headline releases.

Option A: OpenTofu 1.13

Pros

  • Open governance and an MPL-2.0 licence, which simplifies procurement and redistribution questions.
  • Native state and plan encryption with pluggable key providers (including AWS KMS), configured in the terraform block. This matters when state lands in shared buckets.
  • Features that landed after the fork, such as for_each on providers and the -exclude flag, are documented on opentofu.org. Confirm what is in 1.13 against its changelog.
  • Same HCL, same provider protocol, same workflow. The binary is tofu and the commands match, so the CLI is familiar.

Cons

  • Migration becomes effectively one-way once you encrypt state or adopt OpenTofu-only syntax. Terraform cannot read encrypted state, so going back means decrypting with OpenTofu or reverting the config first.
  • Provider and module sourcing goes through the OpenTofu registry. Your .terraform.lock.hcl entries will change, and any tooling hard-coded to registry.terraform.io needs a check.
  • Third-party integrations (scanners, IDE plugins, CI templates) vary in how well they support tofu. Test yours rather than assuming.
  • Commercial support comes from vendors rather than the original authors, which may or may not matter to your org.

Option B: Terraform 1.17

Pros

  • Zero migration. Your pipeline, lock files, runbooks and team habits stay as they are, and upgrades inside the 1.x line follow the documented upgrade guides.
  • First-party integration with HCP Terraform and Terraform Enterprise, including features that exist only there (Stacks, hosted policy and drift tooling). HashiCorp is now part of IBM, so vendor support contracts are a single conversation for many enterprises.
  • Most vendor docs and provider examples still assume terraform, so troubleshooting is easier to search.

Cons

  • The BSL licence restricts offering a competing product. Most internal use is fine, but verify the terms against your situation, especially if you resell infrastructure automation.
  • No built-in client-side state encryption. You depend on backend-side encryption (for example S3 with KMS) and on keeping secrets out of state in the first place.
  • Roadmap and licence decisions sit with one vendor. That is a trade-off, not a defect, but it is a dependency you should name.

Decision matrix

Situation Lean toward Why
HCP Terraform / Enterprise in daily use Terraform 1.17 Platform features are tied to the vendor CLI
Self-managed CI with S3/GCS backend Either Low switching cost; decide on licence and features
Need encrypted state independent of the backend OpenTofu 1.13 Built-in encryption block
Legal requires an OSI-approved licence OpenTofu 1.13 MPL-2.0
Heavy use of vendor-supported Terraform tooling Terraform 1.17 Fewer integration surprises
Config written for a much older Terraform (1.5.x or earlier) Upgrade first, decide later Big version jumps are the risk, not the tool
Small team, no pain today Stay put Switching has a cost and no immediate payoff

Watch out: do not combine a tool switch with a major provider upgrade in one change. When a plan shows unexpected diffs, you want exactly one variable to blame.

Evidence-based recommendation

The evidence for an urgent move in either direction is thin. Both tools plan and apply the same core HCL against the same providers. The differences that matter are licence, state handling, and platform lock-in, and you can evaluate those without touching production.

So the practical approach is a read-only trial. The following runs OpenTofu against a copy of your current setup. It takes a state backup first, then produces a plan and never applies. Run it from a branch.

# 1. Back up current state with the tool you use today
terraform state pull > ../backup-$(date +%F).tfstate

# 2. Check versions; OpenTofu evaluates required_version against its own
#    version number, so review any terraform { required_version } constraint
terraform version
tofu version

# 3. Re-init with OpenTofu (rewrites .terraform.lock.hcl; review the diff)
rm -rf .terraform
tofu init
tofu providers lock -platform=linux_amd64 -platform=darwin_arm64

# 4. Read-only comparison: -lock=false avoids blocking teammates; never apply here
tofu plan -lock=false -detailed-exitcode
echo "exit code: $?"

To verify the result, check the exit code. 0 means no changes, which is what you want from an unchanged codebase. 2 means a diff, so read it before you do anything else. 1 is an error, typically a provider source or backend setting. If the plan is clean, run the same steps in CI on a non-production workspace.

Watch out: run tofu apply only on a workspace you can afford to roll back. A newer tool can write state that the older one refuses to read, and the backup from step 1 is your way out.

If the trial is clean and you want OpenTofu's encryption, add it deliberately as a separate change. Existing state is unencrypted, so the first configuration needs an unencrypted fallback to read it. This example uses AWS KMS; the key alias is a placeholder for your own.

# encryption.tf (OpenTofu only; Terraform will reject this block)
terraform {
  encryption {
    key_provider "aws_kms" "main" {
      kms_key_id = "alias/tofu-state"   # your KMS key alias
      region     = "eu-west-1"
      key_spec   = "AES_256"
    }

    method "aes_gcm" "default" {
      keys = key_provider.aws_kms.main
    }

    # Temporary: lets OpenTofu read the existing unencrypted state
    method "unencrypted" "migrate" {}

    state {
      method = method.aes_gcm.default
      fallback {
        method = method.unencrypted.migrate
      }
    }
  }
}

After the next tofu apply writes encrypted state, remove the unencrypted method and the fallback block, and add enforced = true inside state so OpenTofu refuses to write unencrypted state. Verify by reading the raw object from the backend rather than through tofu, which decrypts transparently, for example aws s3 cp s3://<your-state-bucket>/<your-state-key> - | head -c 200. Encrypted state is a JSON envelope with encryption metadata instead of readable resource attributes. Details, plan encryption and rollback options are in the OpenTofu documentation, so follow those rather than improvising.

The recommendation, with conditions:

  • Stay on Terraform 1.17 if you use HCP Terraform or Enterprise, or if nothing in the matrix hurts today.
  • Move to OpenTofu 1.13 if licence terms or state encryption are real requirements, and the dry-run above shows a clean plan.
  • Do neither yet if you are several minor versions behind. Upgrade within your current tool first, then compare.

For related infrastructure patterns, browse more guides on kuryzhev.cloud. Whichever way you go, pin the binary version in CI so the choice stays a decision and never becomes drift.

Related

Top comments (0)