Terraform state
Terraform state is the record of what Terraform believes it has built. Lose it and every subsequent plan is wrong; share it carelessly and two engineers can overwrite each other's work.
What state is
Every apply records the resources Terraform manages, their attributes and their
dependencies. Before terraform plan or terraform apply, Terraform compares the
configuration against the state to determine what changed.
The state file can contain sensitive values — resource attributes, generated passwords, connection strings — which is why it is never committed to Git and never stored locally for team projects.
Local versus remote
| Local state | Remote state | |
|---|---|---|
| Suitable for | A single developer, disposable infrastructure | Any team, any shared environment |
| Collaboration | None — two people overwrite each other | Locked, shared, consistent |
| Recovery | Manual file handling | Versioned in S3 |
| Security | File sits on a laptop | Encrypted at rest, access-controlled |
Rule. If more than one person works on a Terraform project, state is remote.
How remote state works
terraform init → connects to the backend, downloads current state
terraform plan → reads state, acquires lock, produces a plan
terraform apply → applies, writes new state, releases lock
The S3 backend stores the state object. DynamoDB provides the lock: before any operation that writes state, Terraform acquires an item in the lock table. If the lock cannot be acquired, the operation stops.
Locking
Locking prevents concurrent writes. It is on by default for backends that support it and should not be disabled.
-lock=falsebypasses locking. It is not used except in a documented recovery.terraform force-unlock <LOCK_ID>clears a stale lock left by an interrupted run. Confirm no other operation is in progress first — force-unlocking during a live apply can corrupt state.
Bootstrap
The bucket and lock table cannot be created by the state they store. Qbits provisions them with a dedicated bootstrap module per environment, and commits that bootstrap state to Git: it contains no sensitive data and the resources it manages never change after creation.
Workspaces
Terraform creates a separate state per workspace. Workspaces are useful for
short-lived, identical copies of an environment. They are not a substitute
for environment separation in a shared production estate: a mistyped workspace
name applies production changes to the wrong target. Qbits uses separate
directories with separate state keys for dev, stage and prod.
Practices
- Always use a remote backend for shared projects.
- Run
terraform state pullbefore starting work, so the local view is current. - Never edit the state file by hand. Use
terraform state mv,rmandimportwith a reviewed plan. - Enable bucket versioning so a bad state write can be recovered.
- Encrypt state at rest with SSE-KMS.
- Restrict who can read state: it can expose sensitive values.
- Keep one state per environment and per independently deployable component.
- Run
terraform planin the pipeline and review the plan, not only the diff.
Incidents
| Symptom | Cause | Response |
|---|---|---|
| "Error acquiring the state lock" | Another run in progress, or a crashed run left a lock | Check CI and local sessions; force-unlock only after confirming nothing is running |
| Plan wants to recreate everything | State lost or moved to a different key | Stop. Restore the correct state from the versioned bucket before applying |
| Resource exists but Terraform does not know | Created outside Terraform | terraform import with a reviewed configuration, or remove and recreate |
| State drift | Console changes | Reconcile via code; investigate how the console access happened |