Terraform State Locking, Stuck Locks, and Force Unlock
Posted on Sat 21 March 2026 by Sanyam Khurana in Terraform
If you've worked with Terraform in a team, you've almost certainly run into this error at some point:
Error: Error acquiring the state lock
Lock Info:
ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Path: s3://my-terraform-state/prod/terraform.tfstate
Operation: OperationTypeApply
Who: sanyam@sanyam-macbook
Version: 1.7.0
Created: 2026-03-20 14:32:01.123456 +0000 UTC
Info:
Terraform acquires a state lock to protect the state from being
written by multiple users at the same time. Please resolve the
issue above and try again.
The first time you see this, it can be confusing. Why is Terraform refusing to run? What is this lock? And more importantly, how do you get out of this situation?
Let's break it all down.
Why Does Terraform Lock State?
Terraform state is the single source of truth for your infrastructure. It maps your configuration to the actual resources running in the cloud. When you run terraform plan or terraform apply, Terraform reads and writes to this state file.
Now imagine two team members run terraform apply at the same time. Both read the state, both make changes, and both try to write back. The result? One person's changes overwrite the other's. Resources might get duplicated or orphaned. It's a mess.
State locking prevents this. Before any operation that could write to the state, Terraform acquires a lock. If someone else already holds the lock, your operation will wait or fail. It's the same concept as a database lock - only one writer at a time.
Where is the Lock Stored?
The lock lives in your backend. Not all backends support locking, but the common ones do:
| Backend | Lock Mechanism |
|---|---|
| S3 | DynamoDB table |
| GCS | Built-in object locking |
| Azure Blob | Blob lease |
| Consul | KV store lock |
| Terraform Cloud | Built-in |
| PostgreSQL | Advisory locks |
For S3 (the most common setup), you need a DynamoDB table alongside your S3 bucket. Your backend config looks something like:
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
The dynamodb_table is where the lock record lives. Without it, you have no locking, and you're back to the "two people applying at once" problem.
How Locking Works
The flow is simple:
- You run
terraform apply - Terraform attempts to write a lock record to your backend (e.g., DynamoDB)
- If no lock exists, Terraform creates one and proceeds
- If a lock already exists, Terraform shows the error and refuses to proceed
- When the operation completes (success or failure), Terraform releases the lock
The lock record contains metadata: who created it, when, what operation, and a unique lock ID. This is exactly what you see in that error message at the top of this post.
When Locks Get Stuck
Here's where things get interesting. Locks can get stuck. Common reasons:
- Your terminal crashed mid-apply. Terraform never got the chance to release the lock.
- Network interruption. The apply finished but the unlock request never reached DynamoDB.
- CI/CD pipeline killed. Your CI runner was terminated or timed out while Terraform was running.
- Your laptop went to sleep during an apply (yes, this happens more often than you'd think).
When a lock is stuck, nobody on your team can run Terraform against that state. It's effectively blocked until someone deals with it.
Diagnosing a Stuck Lock
Before jumping to force-unlock, take a moment to check if the lock is actually stuck:
1. Look at the lock info in the error message
Who: sanyam@sanyam-macbook
Created: 2026-03-20 14:32:01.123456 +0000 UTC
Operation: OperationTypeApply
Is this lock from hours ago? Or was it created 30 seconds ago by a colleague who's still running? Check with your team first. Don't force-unlock a lock that someone is actively using.
2. Check your CI/CD pipelines
Is there a running pipeline that might be holding the lock? Check your GitHub Actions, GitLab CI, or whatever your team uses. A pipeline stuck in a "running" state could be the culprit.
3. Check the DynamoDB table directly (for S3 backend)
aws dynamodb scan --table-name terraform-locks
This shows you the actual lock records. You can see exactly what's in there.
Force Unlock
Once you've confirmed the lock is genuinely stuck and nobody is actively using it, you can force-unlock:
terraform force-unlock LOCK_ID
The LOCK_ID is the ID from the error message. In our example, it's a1b2c3d4-e5f6-7890-abcd-ef1234567890.
terraform force-unlock a1b2c3d4-e5f6-7890-abcd-ef1234567890
Terraform will ask for confirmation:
Do you really want to force-unlock?
Terraform will remove the lock on the remote state.
This will allow local Terraform commands to modify this state, even though it
may still be in use. Only 'yes' will be accepted to confirm.
Enter a value: yes
Type yes and the lock is removed.
If you want to skip the confirmation prompt (useful in scripts, but be careful):
terraform force-unlock -force a1b2c3d4-e5f6-7890-abcd-ef1234567890
What If force-unlock Doesn't Work?
Sometimes terraform force-unlock itself fails. Maybe your Terraform state config has changed, or there's a backend connectivity issue. In these cases, you can remove the lock directly from the backend.
For S3 + DynamoDB:
aws dynamodb delete-item \
--table-name terraform-locks \
--key '{"LockID": {"S": "my-terraform-state/prod/terraform.tfstate"}}'
For GCS:
The lock is stored as a .tflock file next to your state:
gsutil rm gs://my-terraform-state/prod/terraform.tfstate.tflock
For Azure Blob:
You need to break the lease on the blob:
az storage blob lease break \
--blob-name prod/terraform.tfstate \
--container-name tfstate \
--account-name mystorageaccount
These are last-resort options. Always try terraform force-unlock first.
Preventing Stuck Locks
Prevention is better than cure. Here are some things you can do:
1. Use Terraform Cloud or Terraform Enterprise
These handle locking natively with proper timeout mechanisms. If a run crashes, the lock is released automatically after a timeout.
2. Add timeouts to CI/CD pipelines
If your Terraform runs in CI, make sure the pipeline has a reasonable timeout. And more importantly, add a cleanup step that runs even on failure:
# GitHub Actions example
jobs:
terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Terraform Apply
run: terraform apply -auto-approve
timeout-minutes: 30
3. Use -lock-timeout
Instead of failing immediately when a lock is held, you can tell Terraform to wait:
terraform apply -lock-timeout=5m
This will retry acquiring the lock for 5 minutes before giving up. Useful when a colleague's apply is about to finish.
4. Always run Terraform in a clean environment
Avoid running long-lived Terraform processes on your laptop where a sleep or network drop can leave orphaned locks.
State Lock and Plan Files
A quick note on how locks interact with plan files. When you run terraform plan -out=tfplan, Terraform acquires a lock only for the duration of the plan. When you later run terraform apply tfplan, it acquires a new lock for the apply.
The plan file itself doesn't hold a lock. So you can generate a plan, review it for as long as you want, and apply it later without worrying about lock timeouts.
Disabling Locking (Don't Do This)
Terraform provides a -lock=false flag:
terraform apply -lock=false
This skips locking entirely. While this "works" to get around a stuck lock, it's dangerous. If two people do this simultaneously, you'll corrupt your state. The only valid use case is when you're the only person with access to the state and you know what you're doing. Even then, just use force-unlock instead.
Summary
State locking is a critical safety mechanism in Terraform. It prevents concurrent modifications that can corrupt your infrastructure state. When locks get stuck:
- Don't panic - check if someone is actively using it
- Use
terraform force-unlockwith the lock ID from the error message - If that fails, remove the lock directly from the backend (DynamoDB, GCS, Azure)
- Prevent future issues with proper CI/CD timeouts and
-lock-timeout
And please, resist the urge to use -lock=false. Your team and your infrastructure will thank you.
If you've any questions about Terraform state locking, please let us know in the comments section below.