State & the plan/apply workflow

init, plan, apply — the same loop.

Beginner12 min · lesson 4 of 12

State is OpenTofu’s memory — a JSON record mapping your configuration to the real resources it manages, so it can compute the difference between what you declared and what exists. The workflow around it is identical to Terraform: init prepares the working directory, plan shows the diff, apply makes it real, destroy tears it down. The plan is the safety mechanism you read before every apply.

The core loop
1init
providers + backend
2plan
diff vs state
3apply
make it real
4state
record reality
Read the plan before every apply. The symbols matter: + create, ~ update, -/+ destroy-and-recreate.
terminal
$ tofu apply
Tofu will perform the following actions:
# aws_s3_bucket.logs will be created
+ resource "aws_s3_bucket" "logs" {
+ bucket = "acme-logs-prod"
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions? yes
aws_s3_bucket.logs: Creation complete after 2s

The state file is compatible with Terraform’s, which is exactly what makes migration painless — but it is also as sensitive as ever, because it can contain resource attributes including secrets in plaintext. Local state (terraform.tfstate on disk) is fine for learning; teams move to a remote backend, and OpenTofu adds the option to encrypt that state, both covered later.

State is sensitive — and the -/+ symbol is dangerous
Never commit terraform.tfstate to Git: it can hold secrets in plaintext. And in any plan, watch for -/+ (destroy and recreate) on a stateful resource like a database or volume — it means data loss and downtime, not an in-place edit. Read the plan; do not just type yes.