Terraform S3 Backend Missing DynamoDB Lock Table - Unsafe Concurrent Writes in CI
An S3 backend with no dynamodb_table (and no native lockfile) does not lock state. Two CI runs can write at once and clobber each other, corrupting or rolling back state.
What this error means
Parallel Terraform jobs both "succeed" but the resulting state is missing resources one of them created, or a later plan shows surprise re-creates. There was never an "Error acquiring the state lock" because nothing was enforcing a lock.
# No lock error is raised - that is the problem.
# Two concurrent applies each read state, mutate, and write back,
# and the last writer overwrites the other's changes:
# run A: writes state v42 (adds aws_instance.web)
# run B: writes state v42 (adds aws_db_instance.main), losing webCommon causes
No dynamodb_table configured on the S3 backend
Older S3 backends require dynamodb_table for locking. Omitting it (and not enabling the native S3 lockfile) means concurrent writes are not serialized.
Unserialized concurrent pipelines
Two PR pipelines, or a developer and CI, target the same state with no locking and no CI concurrency group, so writes race.
How to fix it
Enable locking on the S3 backend
Add a dynamodb_table (or, on newer Terraform, the native S3 lockfile) so concurrent writes are blocked.
terraform {
backend "s3" {
bucket = "my-tf-state"
key = "app/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks" # enables locking
# Terraform 1.10+: use_lockfile = true (native S3 lock)
}
}Serialize Terraform jobs as defense in depth
Add a CI concurrency group so two runs never target the same state simultaneously.
concurrency:
group: terraform-${{ github.ref }}
cancel-in-progress: falseHow to prevent it
- Always configure locking (
dynamodb_tableoruse_lockfile) on S3 backends. - Serialize Terraform jobs with a concurrency group.
- Enable S3 versioning so a clobbered state can be recovered.