Technical log · Terraform
Terraform with remote state: when the local backend stops working
Locking, concurrency, and teamwork on the same infrastructure
When a single person runs terraform apply, keeping the file terraform.tfstate in the project folder is convenient and bothers no one. The problem appears the day two engineers touch the same infrastructure a few hours apart: one applies a change, the other already had a plan built on the previous state and applies it on top. The result is not a clean error, it is a plan that looks valid but destroys or recreates resources that the first one had just created.
The signs that local state is no longer enough are quite recognizable. Orphaned resources appear that no one remembers creating, plans that show unexpected destruction of volumes or buckets, and "resource already exists" messages when importing something that was theoretically already managed. In small teams it often happens that someone copied the state file to their machine to "check something" and since then there have been two versions circulating. When that happens, the state stops being a source of truth and becomes just another file that is synced by hand.
The remote backend solves two things at once: it centralizes state in a single place and adds locking, that is, a lock that prevents two apply runs from running in parallel on the same state. The minimal configuration is a backend block inside the terraform block, pointing to a bucket or a storage service with locking support. Before moving the file you have to make a copy, run terraform init -migrate-state and confirm that the subsequent plan still shows exactly the same changes as before the migration. If the plan changes, something was read incorrectly and it is best to stop.
An overlooked detail: locking does not replace the discipline of reviewing the plan. A remote backend prevents two people from stepping on the same state, but it does not prevent someone from applying a change they did not review. The pre-migration checklist includes verifying that there is no apply in progress, that the local state is up to date, that sensitive variables are not hardcoded in the file, and that the chosen backend has versioning enabled so you can roll back if something goes wrong.