Remote Backends & Collaboration
A backend decides where state is stored and how locking works. Move from the local backend to S3 + DynamoDB or Terraform Cloud so teams share one safe source of truth.
What is this?
A backend defines where Terraform stores its state and how locking is handled. The default local backend writes terraform.tfstate in the working directory. Remote backends — S3, Azure Blob, GCS, etcd or Terraform Cloud — store state remotely and add locking so multiple people can safely run plan/apply on the same infrastructure.
Why do you need it?
- Shared state lets a team work on the same infrastructure from any machine.
- Locking prevents concurrent writes that would corrupt the state file.
- Remote state survives a lost laptop and is encrypted at rest.
- CI/CD can apply from any runner because state is not tied to a local file.
- Backend upgrades like s3 to Terraform Cloud are non-destructive migrations.
Mental model
A team's shared git repository
A remote backend
Everyone pulls from and pushes to the same repository instead of emailing files. Terraform teams pull state, acquire a lock, apply, and push back — the lock is the merge protection that prevents clobbering.
How it works
- terraform init reads the backend block and configures where state lives.
- On every plan/apply, Terraform downloads the latest state and acquires a lock.
- After apply, the updated state is uploaded and the lock is released.
- The S3 backend pairs with a DynamoDB table for locking; Azure uses blob leases.
- Partial configuration keeps credentials and bucket names out of version control via -backend-config.
Remote backend with locking.
Key commands
terraform initInitialize the backend and download providers.
terraform init -reconfigureRe-initialize the backend after changing its configuration.
terraform init -backend-config=backend.tfvarsInitialize with partial configuration values from a file.
terraform force-unlock <LOCK_ID>Release a stale state lock (only after verifying no run is active).
terraform state pullDownload the current remote state to stdout.
terraform state pushUpload a local state file to the remote backend.
terraform providers lockWrite the dependency lock file for reproducible provider versions.
Examples
An S3 backend with locking and partial config
terraform { backend "s3" {}} # backend.tfvars (kept out of git, or via -backend-config)bucket = "my-org-terraform-state"key = "environments/prod/terraform.tfstate"region = "us-east-1"dynamodb_table = "terraform-locks"encrypt = true # init with partial configuration# terraform init -backend-config=backend.tfvarsThe backend block stays empty in code. Sensitive or environment-specific values are supplied at init time with -backend-config, so the same repo works for dev and prod while nothing secret lands in git.
In production
- S3 + DynamoDB is the standard AWS remote backend: durable, cheap, versioned and lockable.
- Terraform Cloud/Enterprise manages state, locking and run history for you.
- Separate state per environment and per stack so failures stay contained.
- Cross-workspace reads via terraform_remote_state share VPC and network outputs.
Best practices
- Always use a remote backend once more than one person touches the infrastructure.
- Enable bucket versioning so a bad apply can be rolled back from state history.
- Encrypt state with SSE-KMS and restrict bucket/DynamoDB access with IAM.
- Keep bucket names globally unique and enable server-side encryption.
- Store backend configuration secrets in env vars or a git-ignored file, never in the block.
Common mistakes
- Forgetting dynamodb_table, which silently disables locking.
- Committing a backend block with credentials or account IDs inline.
- Not enabling versioning on the state bucket, losing recovery ability.
- Running apply without re-initializing after switching backends.
Common issues
Error: Backend configuration changedThe backend block was edited after the last init.
Re-run terraform init -reconfigure to adopt the new backend settings.
Error acquiring the state lockA lock is held by an active run or left behind by a crashed one.
Check for an active run, then terraform force-unlock <LOCK_ID> if stale.
AccessDenied: Access Denied while reading/writing stateThe IAM role or keys lack permissions on the bucket or DynamoDB table.
Grant s3:GetObject/PutObject/DeleteObject on the state object and full access to the lock table.
Debugging tips
- terraform state pull shows exactly what the remote backend holds.
- Check the S3 bucket object and its versions to compare against expectations.
- terraform init -reconfigure reloads backend settings after edits.
- TF_LOG=DEBUG reveals the exact S3/DynamoDB API calls and permissions errors.
Interview checkpoint
Quick quiz
Which pairing provides remote state plus locking on AWS?
What does -backend-config do?
The local backend supports state locking.
Cheat sheet
terraform initInit backendterraform init -reconfigureReload backend configterraform init -backend-config=f.tfvarsPartial configterraform force-unlock IDClear stale lockterraform state pullRead remote stateterraform state pushWrite remote state