DEV Community

Cover image for Terraform S3 Native State Locking - Ditch DynamoDB Forever
Megha Shivhare
Megha Shivhare

Posted on

Terraform S3 Native State Locking - Ditch DynamoDB Forever


No more DynamoDB tables for Terraform locking! Terraform 1.9+ introduced S3 native state locking - a built-in mechanism that eliminates the extra AWS resource while keeping your team deployments safe. Here's everything you need to know.

The Problem with DynamoDB Locking


Traditional S3 backend required two AWS resources:

terraform {
  backend "s3" {
    bucket         = "my-state-bucket"
    key            = "prod/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"  # Extra cost + management ❌
    encrypt        = true
  }
}
Enter fullscreen mode Exit fullscreen mode

Issues:

  • DynamoDB table = always-on cost (~$0.25/month minimum)
  • Extra IAM permissions to manage
  • One more resource to create/delete
  • Lock table drift (table deleted but state remains) ### Enter S3 Native State Locking Terraform 1.9+ (January 2026): use_lockfile = true makes S3 handle locking natively.
terraform {
  backend "s3" {
    bucket       = "my-state-bucket"
    key          = "prod/terraform.tfstate"
    region       = "us-east-1"
    encrypt      = true
    use_lockfile = true  # S3 does it all! ✅
  }
}
Enter fullscreen mode Exit fullscreen mode

How it works:

  1. Terraform creates terraform.tfstate.tflock alongside your state file
  2. S3 manages lock acquisition/release atomically
  3. Optional: Keep DynamoDB for double-locking redundancy
  4. S3 bucket policy needs PutObject for *.tflock files ### Migration: 2 Minutes Flat #### Step 1: Verify compatibility
terraform version  # Must be 1.9.0+
Enter fullscreen mode Exit fullscreen mode

Step 2: Update your backend

# Remove dynamodb_table, add use_lockfile = true
Enter fullscreen mode Exit fullscreen mode

Step 3: Re-init

terraform init -migrate-state
Enter fullscreen mode Exit fullscreen mode

Step 4: Clean up

aws dynamodb delete-table --table-name terraform-locks
Enter fullscreen mode Exit fullscreen mode

Done. Zero downtime, same locking guarantees.

Real-World Test


Scenario: Two terminals, same state file, rapid apply commands.

Before (DynamoDB):

Terminal 1: Acquiring lock via DynamoDB...
Terminal 2: [WAIT] Lock held by Terminal 1
Enter fullscreen mode Exit fullscreen mode

After (S3 Native):

Terminal 1: Acquiring S3 lock...
Terminal 2: [WAIT] Lock held by Terminal 1 (via .tflock)
Enter fullscreen mode Exit fullscreen mode

Identical behavior, one less AWS bill line item.

Gotchas & Requirements

Item Requirement
Terraform 1.9.0+
S3 Permissions PutObject on *.tflock files
Bucket Policy Allow lock file creation
Existing State terraform init -migrate-state

Bucket policy update:

{
  "Effect": "Allow",
  "Action": ["s3:PutObject"],
  "Resource": "arn:aws:s3:::my-state-bucket/prod/*.tflock"
}
Enter fullscreen mode Exit fullscreen mode

Why This Changes Everything

❌ OLD: S3 state + DynamoDB lock = 2 resources
✅ NEW: S3 state + S3 lock = 1 resource
💰 SAVINGS: ~$3/year per state file
🔧 SIMPLICITY: One less failure point
Enter fullscreen mode Exit fullscreen mode

Production teams: Delete 100+ DynamoDB lock tables across your org.
Solo devs: Zero extra resources for remote state.
CI/CD: Simpler IAM roles.

My Updated Workflow (2026)

# Every project now gets this backend
terraform {
  backend "s3" {
    bucket       = "my-org-terraform-state"
    key          = "env/${terraform.workspace}/terraform.tfstate"
    region       = "us-east-1"
    encrypt      = true
    use_lockfile = true
  }
}
Enter fullscreen mode Exit fullscreen mode

Daily habit:

# Verify locks work
terraform plan  # Should show "Acquiring state lock..."
Enter fullscreen mode Exit fullscreen mode

Try It Now

  1. Create S3 bucket: aws s3 mb s3://my-terraform-state-2026
  2. Update your EC2 .tf with above backend
  3. terraform init
  4. Push your local state: terraform init -migrate-state

Result: Locked, remote, DynamoDB-free Terraform in 5 minutes.

S3 native locking makes Terraform state management finally "set it and forget it." No more lock table drift, no more surprise charges, no more complexity.


Already migrated? What was your experience? Still using DynamoDB? Why? 👇

Top comments (0)