Most Terraform horror stories trace back to one thing: state gone wrong. A teammate runs apply on a stale copy and clobbers an hour of work. Someone deletes the state file and Terraform wants to rebuild the entire account. A secret leaks because state was committed to Git. The good news is that solid state management is a small set of rules — and once they're in place, the scary scenarios stop happening. This is that set.
- What state actually is
- Use a remote backend
- Lock state against concurrent writes
- Isolate state by environment
- Treat state as a secret
- The hard rules
- FAQ
What state actually is
Terraform state is a JSON file that maps the resources in your configuration to the real resources in your cloud, plus metadata Terraform needs to plan. When you run a plan, Terraform compares three things: your configuration, the state, and reality. State is the memory that lets Terraform know which real resource a given resource block corresponds to.
If Terraform loses its state, it doesn't lose your infrastructure — but it loses its memory of managing it, and may try to recreate things that already exist.
Use a remote backend
Local state (a terraform.tfstate on someone's laptop) is fine for a solo experiment and a liability for anything else. A remote backend stores state centrally so the whole team works from one source of truth. On AWS, the common choice is an S3 backend:
terraform {
backend "s3" {
bucket = "mycompany-tfstate"
key = "prod/network/terraform.tfstate"
region = "ap-south-1"
encrypt = true
}
}
Enable versioning on the S3 bucket so every state write is retained — that's your undo button if a state operation goes wrong. Restrict the bucket with a tight policy; anyone who can read state can read its secrets.
Lock state against concurrent writes
If two people apply at once, they can corrupt state. State locking prevents that by allowing only one write at a time. Historically teams paired the S3 backend with a DynamoDB table for locks; more recent Terraform and OpenTofu versions also support S3-native lock files. Either way, the principle is the same:
Never run apply against unlocked shared state. The first concurrent write to win corrupts the second.
With locking on, a second apply simply waits (or fails fast) instead of racing — turning a data-corruption bug into a harmless "try again in a minute."
Isolate state by environment
Do not keep production and staging in one state file. A mistake in staging should be incapable of touching production. Isolate state along blast-radius boundaries:
- By environment — separate state for dev, staging, and prod (different keys or buckets).
- By component — networking, data, and application layers in separate states so a change to one doesn't lock or risk the others.
Smaller state files also plan faster and make blast radius obvious. To share values across states (for example, a VPC ID), expose outputs and read them with terraform_remote_state rather than duplicating IDs.
Treat state as a secret
State can contain sensitive values — database passwords, generated keys, certificates — sometimes in plaintext, even if marked sensitive in output. So the file itself is a secret:
- Encrypt at rest (S3 SSE, and consider client-side/state encryption where supported).
- Restrict access with least-privilege IAM on the backend.
- Never commit state to Git. Add it to
.gitignoreand keep it only in the remote backend. - Prefer secrets managers for runtime secrets so fewer of them ever land in state.
The hard rules
- Don't hand-edit state. Use
terraform statesubcommands (mv,rm,import) ormovedblocks — never a text editor. - Back it up. Bucket versioning is non-negotiable; it's your only clean recovery path.
- One source of truth. No local copies floating around — everyone reads and writes the same remote state.
- Refactor with
movedblocks, not destroy-and-recreate, when you rename or restructure resources. - Confirm clean plans. A surprise in
terraform planis often a state problem; investigate before you apply.
If you're just starting to codify an account, you'll generate the configuration first and let Terraform build state as you import. Our guide to generating Terraform from AWS and the import block tutorial cover that step; this article is what keeps the state healthy afterward. InfraSync generates the HCL you'll manage with these practices — read-only, so it never touches your state itself.
Start with clean code, manage clean state.
InfraSync scans your live AWS account and generates production-grade Terraform you bring under your own remote backend — read-only, so your state stays entirely yours.
Start a free scanFAQ
What is Terraform state and why does it matter?
Terraform state maps the resources in your configuration to the real resources in your cloud, along with metadata Terraform needs to plan. It matters because Terraform relies on it to know what it manages; if state is lost, corrupted, or out of sync, Terraform can propose destructive or duplicate changes.
Should Terraform state be stored remotely?
Yes, for any team. A remote backend such as Amazon S3 stores state centrally, supports locking, enables versioning for recovery, and keeps state off individual laptops. Local state is fine only for solo experiments.
Is it safe to store secrets in Terraform state?
State can contain sensitive values in plaintext, so the file must be treated as a secret: encrypt it at rest, restrict access tightly, and never commit it to version control. Prefer generating secrets outside Terraform or using a secrets manager where possible.