When you start running Terraform inside CI/CD pipelines like GitHub Actions, state management becomes a silent troublemaker.
Recently, I hit one of those classic issues:
Error: Error acquiring the state lock
state blob is already locked
And the pipeline refused to continue.
If you’ve ever seen this, trust me you’re not alone. Here’s exactly how it happened, how I fixed it, and what you should always remember to avoid getting stuck like this again.
❗ The Issue: Terraform State Blob Got “Leased”
Terraform uses state locking to prevent multiple processes from modifying the state at the same time.
In my case, the state was stored in Azure Blob Storage.
During a GitHub Actions run:
- The job acquired a lease on the blob
- The job got interrupted
- The lease never got released
So when the next GitHub Actions workflow started, Terraform immediately failed with:
state blob is already locked
When I opened the Azure Blob container, the state file showed:
👉 Leased
That’s when I realized the lock was stuck at the storage level.
🔧 How I Diagnosed It
Three things told me the lock was real:
1. Terraform showed the same lock ID repeatedly
ID: 6a8f9229-xxxx
Who: runner@githubactions
2. My local terraform force-unlock failed
The CLI told me:
local state cannot be unlocked by another process
This showed that the lock wasn’t local it was in Azure.
3. Azure Blob Portal confirmed “Leased” status
Inside the container:
newsraag-prod.tfstate State: Leased
So the GitHub Actions runner died, but the backend still thought it was running.
🛠️ The Fix: Break the Lease Manually
The only clean solution:
👉 Break the lease directly in Azure Blob Storage
Steps:
- Go to Azure Portal
- Open your Storage Account
- Go to Containers → open your Terraform state container
- Click your
.tfstatefile - Choose Break Lease
Within seconds, the blob state changed to:
State: Broken
After this, I re-ran the GitHub Action it worked perfectly.
🧠 What You Should Remember (Very Important)
State lock issues happen in almost every team using Terraform.
Here are the important lessons I learned — and the practices you should follow too.
✅ 1. Never Run Parallel Terraform Jobs for the Same State
If two pipelines hit the same state file → deadlock.
Use GitHub Actions concurrency control:
concurrency:
group: terraform-prod
cancel-in-progress: false
✅ 2. Add Lock Timeout to Terraform Commands
Instead of failing instantly, Terraform will wait for the lock:
terraform plan -lock-timeout=300s
terraform apply -lock-timeout=300s
✅ 3. Don’t Cancel Terraform Pipelines Mid-Way
Cancelling the job leaves Azure Blob leases behind.
Let the job finish unless absolutely necessary.
✅ 4. Use Separate State Files for Each Environment
Do NOT use the same state file for dev, stage, and prod.
Example:
dev.tfstate
stage.tfstate
prod.tfstate
✅ 5. Break Lease ONLY When You’re Sure No Job Is Running
Breaking the lease while Terraform is actively writing = corrupted state.
Always confirm:
- No other Terraform processes
- No pipeline in progress
✨ Bonus: Recommended GitHub Actions Pattern
Here’s the workflow I now use:
concurrency:
group: terraform-${{ github.ref_name }}
cancel-in-progress: false
steps:
- name: Terraform Init
run: terraform init -lock-timeout=300s
- name: Terraform Plan
run: terraform plan -lock-timeout=300s
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -lock-timeout=300s -auto-approve
This avoids 99% of locking issues.
🏁 Final Thoughts
This issue taught me one big lesson state is the heart of Terraform, and anything that interrupts it can break the entire pipeline.
But once you understand:
- how locking works
- where leases get stuck
- how to break them safely
- and how to prevent them
…you’ll never panic when you see the “state blob is already locked” error again.
If you’re using Terraform in CI/CD pipelines, keep this checklist handy. It will save you hours of debugging.
Top comments (0)