Remote state, locking and why it matters
The DevOps Foundation taught Terraform's concepts — providers, resources, plan/apply, and state. On a real AWS project with multiple engineers and environments, state stops being a local file and becomes the thing that can either keep your team safe or cause a catastrophe. Mismanaged state is how two engineers overwrite each other's work, or how someone accidentally destroys production. This lesson is remote state, locking, and why it matters — the first requirement of using Terraform on a team.
Recap: what state is, and why local state fails a team
Terraform's state (the Foundation) is its record of what it has created and how your config maps to real
resources. On your own machine, a terraform.tfstate file is fine. On a team, local state fails immediately:
- Everyone has a different copy. If two engineers each have their own state file, neither knows what the other created; their states diverge and Terraform makes wrong decisions (trying to re-create things that exist, or losing track of things that do).
- No coordination. Two people running
applyat the same time against the same infrastructure, from separate states, corrupt each other's changes. - It is lost easily. A local state file on one laptop is a single point of failure; lose it and Terraform loses track of your entire infrastructure.
So the first thing any team does is move state to a shared remote backend, with locking. This is not optional at scale; it is the foundation of safe collaborative Terraform.
Remote state: one shared, durable state
A remote backend stores the state file in a shared, durable location that everyone (and your CI) uses, so there is one state, not one per laptop. On AWS the standard backend is an S3 bucket (with DynamoDB for locking, below):
terraform {
backend "s3" {
bucket = "mycompany-terraform-state"
key = "production/network/terraform.tfstate" # a path per environment/component
region = "eu-west-1"
dynamodb_table = "terraform-locks" # for state locking
encrypt = true # encrypt state at rest (it can hold secrets)
}
}
Now everyone's Terraform reads and writes the same state in S3. Benefits over local state:
- One source of truth — every engineer and the CI pipeline share the same state, so Terraform always knows the true current state.
- Durable and backed up — S3 is highly durable, and you enable versioning on the bucket so you can recover a previous state if something goes wrong (a corrupted apply). This versioning is your safety net.
- Encrypted — state can contain sensitive values (generated passwords, keys), so it is encrypted at rest
(
encrypt = true, ideally with a KMS key) and the bucket is locked down tightly. Treat the state bucket as sensitive — access to it is effectively access to know (and change) your whole infrastructure.
Locking: stop two applies at once
Shared state solves "different copies", but introduces a new risk: two people (or a person and the CI) running
apply simultaneously against the same state would corrupt it. State locking prevents this: before
making changes, Terraform acquires a lock; anyone else who tries to apply meanwhile is told the state is
locked and must wait. When the first apply finishes, the lock releases.
On AWS with the S3 backend, locking uses a DynamoDB table (the dynamodb_table above): Terraform writes a
lock record there while applying. The effect is that only one change happens at a time, serialised — no two
applies can clobber each other. This is essential once more than one actor (people, CI) can run Terraform, and
it is why the S3 + DynamoDB backend is the classic AWS setup.
(Newer Terraform versions and Terraform Cloud offer built-in state locking without a separate DynamoDB table, and Terraform Cloud/Enterprise provide managed remote state with locking, a UI, and access control as a hosted service — a common alternative to self-managed S3+DynamoDB. The principle is identical: shared state, locked during changes.)
State discipline on a team
A few practices that keep state safe once it is remote and shared:
- Never edit state by hand. The state file is Terraform's internal record; hand-editing it corrupts the
mapping. Use Terraform commands (
terraform state mv,import,rm— the drift lesson) for the rare cases you must manipulate state. - Separate state per environment and component. Do not keep all your infrastructure in one giant state
file — a change to a small thing then locks and risks everything. Split state by environment (prod/staging/
dev) and often by component (networking, cluster, app), each with its own state key (the
keyin the backend). This limits blast radius: a mistake or lock affects only that slice. (The repo-structure lesson builds on this.) - Lock down access to the state backend. Because state reveals and controls your infrastructure (and may hold secrets), restrict who and what can read/write the S3 bucket — typically only the CI role and a small set of engineers, via IAM (the IAM lesson).
- Enable versioning on the state bucket, so a bad state can be rolled back.
The one-line summary: on a team, Terraform state lives in a shared, encrypted, versioned remote backend, is locked during every change, is never edited by hand, and is split per environment/component to limit blast radius. Getting this right is the precondition for everything else in this module — you cannot structure Terraform for multiple engineers and environments until state is shared and locked.
Check your work
Why local state fails a team: divergent per-laptop copies (Terraform makes wrong decisions), no coordination (simultaneous applies corrupt each other), easily lost (single point of failure). Move to a shared remote backend with locking — not optional at scale.
Remote state (S3 backend): one shared, durable state in S3 (a key path per environment/component);
benefits: one source of truth (people + CI), durable + versioned (recover a bad state), encrypted at
rest (state holds secrets — treat the bucket as sensitive, lock it down).
Locking (DynamoDB): before changes Terraform takes a lock; concurrent applies must wait → only one change at a time, no clobbering. Essential once people + CI can both run Terraform. (Terraform Cloud / newer versions offer built-in locking; same principle.)
State discipline: never hand-edit state (use state mv/import/rm); split state per environment/
component (own key) to limit blast radius; lock down backend access via IAM; enable bucket versioning.
Precondition for the rest of the module.
Practice
- Explain three ways local state fails a team, with a concrete consequence for each.
- Write an S3 backend configuration with locking and encryption, and explain each field.
- Explain why the state bucket must be treated as sensitive and locked down.
- Explain what state locking prevents and how DynamoDB provides it in the S3 backend.
- Explain why you split state per environment and component rather than one giant state file.
- Explain why you must never hand-edit the state file, and what to use instead.
Official documentation
- Terraform — State — What state is and why remote state matters.
- Terraform — S3 backend — Remote state on S3 with DynamoDB locking.
- Terraform — State locking — Preventing concurrent state corruption.
Next: structuring a Terraform repository.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship