The S3 backend historically had no locking of its own and relied on a separate DynamoDB table named by dynamodb_table, using a conditional write on a lock item. Terraform 1.10, released November 2024, added use_lockfile = true, which stores a lock object alongside the state in the same bucket and relies on S3 conditional writes.
Migrating is not automatic. Adding use_lockfile while leaving dynamodb_table set makes Terraform acquire both locks, which is the correct transition state while some team members or CI runners are still on an older Terraform. Once every consumer is on 1.10 or later, remove dynamodb_table and delete the table.
Running with neither option set means no locking at all, and Terraform issues no error. Two concurrent applies then interleave writes and the last one to finish overwrites the other's state, orphaning real infrastructure. Confirm locking is active by starting an apply and running a second plan; the second should block with Error acquiring the state lock naming the holder.