linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
Intro
Lessons
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Knowledge base
  • Cheat sheet
  • Capstone
  • Interview prep
home/terraform/kb/Advanced/tf-opentofu-parity

kb/advanced ── Advanced ── advanced

OpenTofu, the Terraform Fork, and Parity Status

In 2023 HashiCorp relicensed Terraform under BSL. The community forked it as OpenTofu under the Linux Foundation. The API is compatible, the HCL syntax is the same, and providers are shared. Divergence has started: OpenTofu added state encryption and `exclude` for for_each; Terraform added Stacks. The practical response is matrix CI: run both and catch feature gaps.

view as markdownaka: opentofu, terraform-fork, tofu, opentofu-vs-terraform

Brief History

  • Aug 2023. HashiCorp relicenses Terraform from MPL-2.0 to BSL (Business Source License), which prohibits commercial alternatives.
  • Sep 2023. The Linux Foundation announces OpenTofu, a fork of the last MPL release (1.5.x).
  • 2024. OpenTofu 1.6 and 1.7 ship; features accumulate in parallel with Terraform, sometimes ahead of it.
  • 2025. Both tools are active; community providers support both.

At 1.5 the two are technically identical. From 1.6 onward, divergence grows.

What Is the Same

  • HCL syntax.
  • All providers (single source, Registry).
  • Commands: init, plan, apply, destroy, fmt, validate.
  • State format and backward compatibility. Migration from terraform state to tofu state requires no conversion.
  • Backends (S3, GCS, local, Terraform Cloud in read-only mode for OpenTofu).
  • The 95% of everyday use cases.

What Is Different

FeatureTerraformOpenTofu
*.tftest.hcl framework1.6+1.6+
mock_provider1.7+1.7+
state encryption at restNo (backend SSE only)Yes (native, since 1.7)
exclude in for_eachNoYes (since 1.8)
StacksYes (HCP-only)No
Terraform Cloud integrationNativeRead-only (via OAuth)
LicenseBSLMPL-2.0
Provider registryregistry.terraform.ioregistry.opentofu.org (mirror)

Migration

Scenario: you are using an HCP-free workflow and want to move away from BSL.

bash
# Terraform was installed like this:
brew install hashicorp/tap/terraform
# OpenTofu:
brew install opentofu
tofu --version

From that point on, use tofu in place of terraform:

bash
tofu init
tofu plan
tofu apply

State is compatible. Running tofu plan against a raw terraform.tfstate produced by Terraform works without conversion. The lockfile (.terraform.lock.hcl) is shared.

If your stack uses 1.6+ features such as mock_provider, do a test run. Parity is good, but edge cases exist.

State Encryption

OpenTofu 1.7+ can encrypt state on the client side before writing it to the backend:

hcl
terraform {
  encryption {
    key_provider "aws_kms" "key" {
      kms_key_id = "arn:aws:kms:..."
      region     = "us-east-1"
      key_spec   = "AES_256"
    }
    method "aes_gcm" "method" {
      keys = key_provider.aws_kms.key
    }
    state {
      method = method.aes_gcm.method
    }
    plan {
      method = method.aes_gcm.method
    }
  }
}

This is a genuine advantage: even if the backend (S3) is compromised, the state stored in it is encrypted. Terraform relies on the backend's SSE, which is sometimes insufficient. See tf-secrets-in-state.

exclude in for_each (OpenTofu 1.8)

hcl
variable "envs" {
  type    = set(string)
  default = ["dev", "stage", "prod", "test"]
}
resource "aws_s3_bucket" "logs" {
  for_each = var.envs
  bucket = "logs-${each.key}"
  exclude {
    condition = each.key == "test"
  }
}

This removes an element from for_each without reindexing. Terraform has no equivalent; you need setsubtract(var.envs, ["test"]).

Matrix CI with Both Tools

The most practical response to the uncertainty is to run both:

yaml
jobs:
  plan-terraform:
    runs-on: ubuntu-latest
    steps:
      - uses: hashicorp/setup-terraform@v3
        with: { terraform_version: 1.9.8 }
      - run: terraform init
      - run: terraform plan
  plan-opentofu:
    runs-on: ubuntu-latest
    steps:
      - uses: opentofu/setup-opentofu@v1
        with: { tofu_version: 1.8.0 }
      - run: tofu init
      - run: tofu plan

If anything diverges, both jobs will show it. For the production apply you pick one tool: whichever owns the state.

This is a safety net. If HashiCorp changes the license again, you have a ready switch.

Pitfalls

  • registry.terraform.io vs registry.opentofu.org. The providers are the same, but the registries are different. OpenTofu defaults to its own; ~/.terraformrc or an environment variable overrides this. For air-gapped environments, mirror both.

  • Terraform Cloud features. Sentinel policies, workspaces, dynamic credentials, and other Terraform Cloud features do not work in OpenTofu. OpenTofu can only read state from Terraform Cloud.

  • Version numbering divergence. Major version numbers do not correspond: OpenTofu 1.7 is not Terraform 1.7. Compare feature by feature, not by version number.

  • hcp-terraform block in configuration. OpenTofu understands the syntax but does not connect. Leave it in place during the migration period; it causes no harm.

  • Tooling divergence. Atlantis, Spacelift, Env0, and similar tools vary in their tofu support. Check before migrating.

  • Provider signatures and checksums. OpenTofu uses its own public keys. If terraform_required_providers specifies an explicit hash, it may not match. Fix: remove the lockfile and reinitialize.

  • The community is already split. An issue filed in one repository may not be reflected in the other. When reporting a bug, file in both.

  • State encryption is opt-in. Once enabled, every team member needs KMS access. Without KMS decrypt, no one can work with the state. Recovery after key loss is significantly harder.

§ команды

bash
tofu --version

Verify the OpenTofu installation.

bash
tofu init

Equivalent of terraform init.

bash
tofu plan

Equivalent of terraform plan. Reads state produced by Terraform.

bash
TF_PLUGIN_CACHE_DIR=~/.tf-cache tofu init

Cache providers across tofu and terraform runs to save bandwidth.

§ см. также

  • tf-stateState: Terraform's memory of what it createdState is the JSON file `terraform.tfstate` where Terraform records what it created in the cloud. Without it, Terraform would have no way to tell which bucket is "its own" and which belongs to something else. The file holds resource IDs, all attributes, and often secrets. It is the most sensitive part of any project.
  • tf-remote-backend-s3Remote state in S3: bucket, DynamoDB lock, encryptionS3 backend stores `terraform.tfstate` in a bucket. A DynamoDB table provides locking so only one apply runs at a time. Configuration goes in the `backend "s3"` block inside `terraform { ... }`. State lives in S3. It is the single source of truth; there is no local file anymore. Migrate from local to S3 with `terraform init -migrate-state`.
Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies