Terraform state locking is a safety mechanism that prevents concurrent Terraform runs from modifying the same state file at the same time, which could corrupt your infrastructure’s recorded state. When you run a command like terraform apply, Terraform acquires a lock on the state; if another run tries to acquire it, that run waits or fails instead of overwriting the state. This article explains why the lock matters, how it works in different backends, and how to handle common lock-related issues.
Why State Locking Exists
Terraform stores the desired state of your infrastructure in a state file (usually terraform.tfstate). This file maps your configuration to real-world resources. If two people run terraform apply at the same time, both read the same state, both plan changes, and both write their own version. The last write wins, and the other person’s changes are lost—or worse, the state file becomes inconsistent with reality. Locking ensures that only one run can modify the state at a time, so changes are serialized and consistent.
How Terraform Locking Works
When you run a state-modifying command (apply, destroy, refresh, import, etc.), Terraform attempts to acquire a lock on the state. The lock is stored in the backend that holds the state file. For example:
- Local backend: Creates a
.terraform.tfstate.lock.infofile in the same directory. This only prevents concurrent runs on the same machine. - S3 backend: Uses DynamoDB to store a lock record. You must create a DynamoDB table with a primary key named
LockID(type String) and configure the backend withdynamodb_table. - AzureRM backend: Uses a blob lease on the state blob in Azure Storage. No extra configuration needed.
- GCS backend: Uses a GCS object lock. No extra configuration needed.
- Consul backend: Uses a Consul session lock on a key-value path.
If the lock is already held, Terraform will wait up to the -lock-timeout duration (default 0 seconds, meaning it fails immediately). If the timeout is exceeded, it returns an error like Error: Error acquiring the state lock.
When Locking Matters Most
Locking is critical in any team environment where multiple people or CI/CD pipelines run Terraform against the same state. Without it, you risk:
- Lost updates: Two runs both create the same resource, or one deletes what the other just added.
- State corruption: Interleaved writes can produce a state file that doesn’t parse or that references non-existent resources.
- Inconsistent plans: A plan based on stale state can lead to unexpected changes or failures.
Even solo users benefit from locking when running Terraform from multiple terminals or from a cron job.
How to Choose a Locking Backend
If you’re using a remote backend, ensure it supports locking. Here’s a quick comparison:
- S3: Requires DynamoDB; if you forget the table, locking is disabled and you get no warning.
- AzureRM: Locking is automatic; no extra setup.
- GCS: Locking is automatic; no extra setup.
- Consul: Requires a Consul cluster; good for HashiCorp shops.
- HTTP: Does not support locking (as of Terraform 1.x).
- Local: Only works on a single machine; not suitable for teams.
For most teams, S3+DynamoDB or AzureRM are the standard choices because they’re reliable and integrate with existing cloud IAM.
Common Problems and Solutions
Stale Locks
If a Terraform run crashes or is killed (e.g., Ctrl+C, OOM, network drop), the lock may remain. Terraform has a -lock-timeout option to wait, but if the lock never releases, you can force-unlock with:
terraform force-unlock <LOCK_ID>
To find the lock ID, run terraform plan and look at the error message: it will include the lock ID. Use this command only when you’re sure no other run is active.
Lock Timeout Too Short
If your runs often take longer than the timeout, set -lock-timeout=10m or configure it in the backend block with lock_timeout.
DynamoDB Table Missing
If you use S3 and forget to create the DynamoDB table, Terraform will not lock, and you’ll get no error. Always verify the table exists and has the correct key schema.
Locking in CI/CD
In pipelines, use the -lock-timeout flag to avoid immediate failures when a human is running Terraform at the same time. Also, consider using separate state files per environment to reduce contention.
Alternatives to Locking
Locking is not the only way to manage concurrent access. Some teams use:
- Workspaces: Separate state files per workspace, so concurrent runs on different workspaces don’t conflict.
- Remote state with versioning: S3 versioning or Azure blob snapshots let you recover from a bad write, but they don’t prevent the bad write.
- Terragrunt: Adds its own locking layer (via DynamoDB) and can run commands serially.
- Atlantis: A CI tool that queues Terraform runs, effectively serializing them.
Locking remains the simplest and most direct solution; alternatives either add complexity or don’t fully prevent corruption.
Costs and Safety Considerations
Locking itself is free—it’s just a file or a database record. The only cost is the DynamoDB table if you use S3 (a few cents per month). However, locking can cause delays if runs are long and the timeout is high. Always set a reasonable timeout (e.g., 5-10 minutes) and ensure your team knows to release locks manually if a run is aborted.
Safety-wise, never disable locking (-lock=false) unless you truly understand the risks. It’s meant for special debugging, not routine use. Also, remember that locking only protects the state file, not the actual cloud resources—two runs could still create conflicting resources if they use different state files.
Frequently Asked Questions
How do I check if a state lock is held?
Run terraform plan. If a lock is held, you’ll get an error message like Error: Error acquiring the state lock with details. You can also check the backend directly (e.g., DynamoDB table items or the .lock.info file).
What does “force-unlock” do?
It manually releases a lock without running a Terraform operation. Use it only when you know no other run is in progress. The command requires the lock ID, which you can get from the error message.
Can I use Terraform without state locking?
Yes, by passing -lock=false to commands like apply or plan. This disables locking for that run. It’s not recommended for shared states because it can lead to corruption.
Why does my S3 backend not lock?
Most likely because you didn’t configure a DynamoDB table. Add dynamodb_table = "terraform-locks" to the backend block and create the table with LockID as the primary key.
What happens if two runs try to lock at the same time?
One run acquires the lock; the other waits (if -lock-timeout is set) or fails immediately. Once the first run finishes and releases the lock, the waiting run proceeds.
Does locking work with the local backend?
Yes, but only for runs on the same machine. It creates a lock file in the state directory. It doesn’t help across different machines.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Server Repair Fails: Fix Restart Loops
- Plane Crazy Summer Event Schedule & Dates3 tier
- How to Choose a Secure IMAP Email Client
- Tower Defense RNG Codes: Active List & How to Redeem
- RCS Chats On or Off in Google Messages
- How to Choose a Phone That Supports VoLTE on Your Carrier