Terraform · Part 7

Terraform — Part 7 — Remote State and State Locking

Part 6 closed on local state’s limits: no locking, and tied to one laptop. This part covers the fix — moving state into a shared remote backend, and why locking is what actually makes team use of Terraform safe.

Quick idea: a remote backend moves state off one person’s laptop into shared storage, and locking stops two people from running apply against that same state at the same time.
Backend

Where Terraform stores state — local by default, or a shared remote location like an S3 bucket.

Locking

A mechanism that blocks a second apply from running while one is already in progress against the same state.

Migration

Moving state from one backend to another, handled automatically by terraform init.

Introduction

Part 6 ended on local state’s two limits: it has no locking, and it lives on one machine. Neither is a problem while you’re the only person running Terraform. Both become real problems the moment a second person, or a CI pipeline, needs to run apply against the same infrastructure.

What a Backend Is

A backend determines where Terraform stores its state data. Every configuration has one, whether it’s declared explicitly or not — the default, used implicitly in every example so far in this series, is the local backend, which simply writes terraform.tfstate to the working directory.

A backend is configured inside the terraform block:

terraform {
  backend "s3" {
    bucket = "example-terraform-state"
    key    = "project/terraform.tfstate"
    region = "us-east-1"
  }
}

A configuration can only have one backend block. It also can’t reference variables, locals, or data sources — backend configuration has to be resolvable before Terraform has read any of the rest of the configuration, since it’s what tells Terraform where to even look for state in the first place.

Practical rule: after adding or changing a backend block, run terraform init again. Terraform detects the change and offers to migrate existing state into the new backend automatically.

Why Remote State

A remote backend puts state somewhere every team member, and any CI/CD pipeline, can reach — instead of on whoever happened to run apply first. This is the same “reproducible, not personal” theme from Part 1’s case against click-ops, applied to state itself: infrastructure that only one laptop knows the true status of isn’t meaningfully reproducible.

Common backend types include s3 (AWS), azurerm (Azure Blob Storage), gcs (Google Cloud Storage), and remote for HCP Terraform, alongside several others for platforms like Consul, Kubernetes, and PostgreSQL. Which one you use generally follows which cloud your infrastructure already lives in.

Example: The S3 Backend

The s3 backend takes three required arguments: bucket, the S3 bucket state gets stored in; key, the path to the state file within that bucket; and region, the AWS region the bucket lives in.

What Locking Prevents

Imagine two engineers both run terraform apply against the same remote state within seconds of each other. Without locking, both read the same starting state, both compute a plan, and both start writing changes — and whichever one finishes writing state last silently overwrites the other’s changes, with no error and no warning.

State locking prevents exactly this. When one apply starts, it acquires a lock; a second apply against the same state has to wait until the first one finishes and releases it.

How Locking Works: S3 as an Example

Locking mechanics are backend-specific. On the S3 backend, the current approach is native S3 locking: setting use_lockfile = true makes Terraform write a lock file alongside the state object in the same bucket while an operation is in progress.

terraform {
  backend "s3" {
    bucket       = "example-terraform-state"
    key          = "project/terraform.tfstate"
    region       = "us-east-1"
    use_lockfile = true
  }
}
Real-world variance: older S3 backend configurations you’ll encounter in existing codebases use a separate DynamoDB table for locking instead, with a LockID partition key. That approach is now deprecated in favour of native S3 locking, though it’s still supported and both can be configured together during a migration.

Other backends handle locking through whatever native mechanism their platform offers — the azurerm backend uses Azure Blob Storage’s own lease capability, for instance. The mechanism differs by platform; the guarantee it provides is the same everywhere: only one write in flight against a given state at a time.

Migrating to a Remote Backend

Moving from local state to a remote backend is a matter of adding the backend block and re-running terraform init, which Part 3 already covered as backend initialisation being one of init‘s core jobs:

# After adding or changing a backend block, Terraform detects
# it and offers to copy existing state into the new backend
terraform init

Terraform prompts before copying anything, so existing state isn’t silently abandoned mid-migration.

Quick Reference Summary

Term Meaning
Backend Where Terraform stores state — local by default, or a shared remote store.
Locking Blocks a second concurrent write to the same state.
use_lockfile S3 backend argument enabling native S3-based locking (current recommended approach).
DynamoDB locking The older S3 backend locking mechanism, now deprecated in favour of native locking.
State migration Copying existing state into a newly configured backend, handled by terraform init.

Final Thoughts

Remote state and locking together are what turn Terraform from a personal scripting tool into something a whole team can run safely against the same infrastructure. Neither changes anything about the configuration language itself — they change where state lives and who’s allowed to write to it at any given moment.

Key takeaway: a remote backend answers “where does state live so everyone can reach it,” and locking answers “what stops two people from corrupting it at the same time” — a real team setup needs both.
Next in this series

Terraform — Part 8 — Data Sources vs Resources. We cover how to read information about infrastructure Terraform doesn’t manage, without accidentally trying to create or change it.