Terraform and OpenTofu state locking, explained
State is the record of what OpenTofu manages. Two writers at once can corrupt it, and anyone who can read it can read your secrets. Here is how locking and remote state keep it safe.
What state is, and why it needs a lock
The state file maps each resource address in your configuration to a real object in your cloud, along with its attributes. Every plan reads it and every apply writes it.
If two applies run at once, both read the same state, both change infrastructure, and the last one to finish overwrites the other's record. Resources the first apply created are now missing from state, and the next plan tries to create them again. A lock makes the second writer wait or fail instead.
How locking works
- Before any operation that may write state, OpenTofu asks the backend for a lock and records who holds it.
- If another process holds it, OpenTofu stops with a lock error.
- When the operation ends, OpenTofu releases the lock.
Use -lock-timeout=5m to wait for a lock instead of failing immediately. Never use -lock=false against shared state.
Which backends lock
| Backend | Locking |
|---|---|
| Local file | Only on one machine; not shared |
| S3 | A lock file in the bucket (use_lockfile = true) in recent releases, or a DynamoDB table |
| Google Cloud Storage | Built in |
| Azure Blob Storage | Built in, using blob leases |
| PostgreSQL | Built in, using advisory locks |
cloud block (Terraform Cloud, StackShip) | Built in, shared by remote runs and local commands |
Clearing a stuck lock
A process that crashes or loses its network connection can leave a lock behind. The error shows the lock ID and who took it. Once you are sure that process is gone, release it:
tofu force-unlock LOCK_IDCheck first. Force-unlocking a lock that a live apply holds is exactly the race the lock exists to prevent.
State contains secrets
Database passwords, private keys, and tokens that providers return all end up in state as plain JSON, even when marked sensitive. Treat read access to state as access to those secrets:
- Encrypt it at rest and restrict who can read it.
- Turn on versioning so a bad write can be rolled back.
- Log every read of the raw file.
- Share outputs between configurations rather than the whole state wherever you can.
OpenTofu can also encrypt state on the client before it reaches the backend. See OpenTofu vs Terraform for what else OpenTofu added.
Remote state in StackShip
- One lock covers runs and local
tofucommands, so a laptop apply can't collide with a run in progress. Runs in a workspace also queue one at a time. - Every version is kept. Each apply or
tofu state pushcreates a new version with its serial, resource count, and checksum. - Raw downloads are restricted to owners and admins, need recent sign-in and a reason, and are recorded in the audit log.
- Outputs sharing lets one workspace read another's non-sensitive outputs without seeing its full state.
- Pushes check lineage, even when forced, so state from a different configuration can't overwrite yours.
If an apply is interrupted, the workspace stays locked until an admin confirms which state is correct. See how StackShip protects state.