Terraform or OpenTofu
Terraform or OpenTofu
Two questions get tangled together whenever infrastructure-as-code comes
up, and they deserve to be pulled apart because one is easy and the other
is the thing that actually ruins afternoons. The easy one is which tool,
Terraform or OpenTofu. The hard one, the one neither tool solves for you,
is state. This post answers the first quickly and then spends its time on
the second, because that is where the real work is.
The Tool Question Is Easy Now
The short version: for new work in 2026, use OpenTofu, and do not agonise
over it.
The backstory in two sentences. In August 2023 HashiCorp moved Terraform
from the open-source MPL 2.0 license to the Business Source License, a
source-available license that restricts competing commercial use, and
within days the community forked the last open version into OpenTofu.
That fork is now under the MPL 2.0, governed by the Linux Foundation,
a CNCF project since 2025, and as of mid-2026 it is a mature, stable tool
that most Terraform configurations run on unchanged.
The practical differences are small and mostly favour OpenTofu. Same
configuration language, same providers, same workflow; you type tofu
instead of terraform. OpenTofu has shipped a few things the open
Terraform binary lacks, the most useful being built-in client-side state
encryption, which Terraform leaves to the backend. The license is
OSI-approved open source rather than source-available. The roadmap is set
by a foundation committee rather than one company. For a greenfield
project there is no BSL friction to plan around and no reason not to start
on OpenTofu.
For existing Terraform estates the honest answer is less dramatic than the
internet makes it: the BSL does not prevent ordinary internal use of
Terraform, so there is no fire to put out, and migration is a real if
usually small project. If you are a software vendor embedding the tool, or
your procurement flags the BSL, that is a concrete reason to move. For
most internal users it is a sovereignty choice, you would rather depend on
a community-governed open-source tool than a single vendor's
source-available one, and I find that reason sufficient on its own. But it
is not an emergency, and it is not the interesting part.
Because whichever one you pick, they share the same architecture, the same
power, and the same single hardest problem: state.
What State Actually Is
Terraform and OpenTofu are declarative. You write what the infrastructure
should look like, and the tool makes reality match. To do that, it has to
know what it already created, and that knowledge lives in a state file: a
JSON record mapping every resource in your configuration to the real
object it created in the cloud, with its ID and its current known
attributes.
State is what makes the whole model work. When you change your
configuration and run a plan, the tool compares three things: what you now
say you want, what the state says it last created, and, by refreshing,
what currently exists. The difference becomes the plan. Without state, the
tool would have no idea whether a resource in your config is something it
should create or something it already made, and it could not compute a
sensible change. State is the memory of the system.
It is also, for exactly that reason, the most dangerous file you own. It
is the single source of truth about your infrastructure, and if it is
lost, corrupted, or wrong, the tool's view of reality is wrong, and the
actions it takes to "fix" the difference can be catastrophic. Most real
infrastructure-as-code disasters are not syntax errors. They are state
problems.
The Three Ways State Bites
Knowing the failure modes in advance is most of what separates a calm IaC
practice from a scary one.
State lives in the wrong place. The default is a terraform.tfstate file
on your laptop. The instant a second person runs a command, or the instant
your laptop dies, that is a disaster: two people with two local states,
each thinking they own reality, applying conflicting changes. State
belongs in a remote backend, an S3 bucket, an Azure storage account, a GCS
bucket, shared and versioned, so there is one copy and a history to roll
back to. This is the first thing to set up and the one people skip because
local just works until it very suddenly does not.
Two runs touch state at once. If two people, or two CI pipelines, apply at
the same time against the same state, they race, and a race on your
infrastructure's source of truth corrupts it: half of one change
interleaved with half of another, a state file that describes a reality
that never existed. The protection is state locking. Before any operation
that writes state, the tool takes a lock, and anyone else is made to wait
rather than run concurrently. Modern backends handle this natively, and
getting it right is not optional, an unlocked shared state is a corruption
waiting for the first unlucky overlap. OpenTofu's recent S3 native locking
removed the old need for a separate DynamoDB table just to hold the lock,
which is one less moving part.
Reality drifts from state. This is the deep one, and neither tool solves
it, because neither can. State records what the tool deployed. It does not
know what exists right now. When someone makes a change by hand in the
cloud console, an emergency fix at 3am, a setting toggled in a hurry, a
resource created out of band, reality and state diverge. That gap is
drift, and the tool is blind to it until the next refresh, at which point
your next plan may try to "correct" the manual fix back to what the config
says, undoing the emergency change, or may show a confusing diff nobody
expected. Drift is not a tooling bug; it is the fundamental limitation of
any state-based system. The only real defence is discipline, all changes
go through the code, never the console, plus detection, scheduled drift
checks that compare state to reality and alert on the difference before it
becomes a surprise during an apply.
State Holds Your Secrets, Too
One more thing people discover the hard way: the state file often contains
secrets in plaintext. A database password, a generated key, an access
token that a resource produced, if it is an attribute of a managed
resource, it is sitting in the state JSON in the clear. That makes the
state file not just operationally critical but a confidentiality problem.
A state file in an unencrypted bucket, or committed to git in a moment of
carelessness, is a credential leak. Encrypt state at rest in the backend
at minimum, and this is where OpenTofu's built-in client-side encryption
genuinely helps, it encrypts the state before it ever leaves your machine,
so the backend only ever holds ciphertext, independent of whatever the
storage provides. Treat the state file with the same care as the secrets
it contains, because it contains them.
The Point
Choosing between Terraform and OpenTofu is the easy decision, and in 2026
the easy answer is OpenTofu for anything new: open-source license,
community governance, drop-in compatibility, and a couple of useful extras.
Make that call in five minutes and move on, because it is not where the
difficulty lives.
The difficulty lives in state, and it is identical for both tools, because
they share the same design. State is the memory that makes declarative
infrastructure possible and the single most dangerous file you own. Put it
in a shared, versioned, encrypted remote backend from day one. Lock it so
concurrent runs cannot corrupt it. Accept that drift is real and defend
against it with the discipline of changing infrastructure only through
code and the safety net of scheduled drift detection. And remember it holds
your secrets in the clear, so protect it accordingly.
Get the state model right and infrastructure-as-code is the calm,
boring, reproducible thing it is supposed to be. Get it wrong and no choice
of tool will save you, because the tool was never the hard part. The state
was.