Terraform Remote Backends
One source of truth, guarded
Remote backends centralize state for teams: shared storage, locking, versioning, and audit. S3/GCS/Azure blob + lock table is the de-facto; Terraform/OpenTofu Cloud adds run orchestration, plan artifacts and policy.
What you will learn
- Compare S3/GCS/Azure/cloud backends
- Configure backend + lock + encryption hardening
- Handle state lifecycle: migrate, clone, rollback
Backend = where state lives + locking + history. S3: bucket+key+lock, cheap and standard. Cloud: run engine (plan/apply/approval/states in UI) with real collaboration features. Choose by team maturity and governance needs.
What is this?
Backend feature matrix: shared storage (bucket/blob/GCS), locking (DynamoDB/GCS conditions/Az lease or cloud runs), versioning (object versions / run history), workspaces, per-key isolation, concurrency. S3 backend: region, key, bucket, dynamodb_table, encrypt, kms_key, role_arn, profile. GCS: bucket, prefix, impersonation. Azure: resource_group+container+storage_account_name+key. Terraform Cloud/OpenTofu: state per workspace + run queue (VCS integration), Sentinel/OPA policies, notifications, isolation via API/EFF. Migration: init -migrate-state; rolling back a state version = downloading prior object + re-init.
Why do you need it?
- A team can't coordinate against a laptop file; shared+locked state is the baseline.
- Versioned state means a bad apply is reversible in minutes.
- Cloud backends add run orchestration (plan comments, approvals, audit) without bespoke CI glue.
- Per-backend encryption + restricted IAM turns the state bucket into a vault, not a leak.
Mental model
Company's master ledger kept in a vault with one teller, vs handwritten scraps per desk
Remote backend vs local state
One vault ledger (locked, audited, versioned) coordinates every teller; desk scraps (local state) guarantee two tellers will balance different truths.
How it works
- init reads backend block (via -backend-config for secrets), acquires a lock during state ops.
- Every plan/apply pulls state, writes back with version bump.
- Cloud runs wrap the same with VCS triggers, approval approval models, and policy evaluation.
- Object versioning (S3/GCS) preserves history for rollback.
- Migration moves existing state into the backend cleanly via -migrate-state.
Backend topology.
Examples
Hardened S3 backend
terraform { backend "s3" { bucket = "acme-tfstate" key = "stack/prod.tfstate" region = "us-east-1" dynamodb_table = "tf-locks" encrypt = true kms_key_id = "arn:aws:kms:us-east-1:...:key/xyz" role_arn = "arn:aws:iam::...:role/tf-state" }}# bucket policy: deny delete-without-mfa + deny unencrypted PUTEncryption, locking, versioning, and least-privilege IAM all roll into the backend definition.
In production
- Per-env S3 keys under one hardened bucket (dev can't lock prod state) — narrow IAM.
- Terraform Cloud run queue: every PR gets plan/apply orchestrated with approvals and audit.
- Multi-account think: state role per account, backend bucket owned by a platform account.
Best practices
- Use backend-config files for secrets instead of hardcoding in code.
- Enable bucket versioning + lock and object encryption (KMS where sensitive).
- Scope backend IAM per team/env; never a single all-state role spread repo-wide.
- Document the migration path (init -migrate-state) and run-history retention.
- Choose cloud backends where process matters more than infra plumbing; S3 for pure-IaC teams.
Common mistakes
- Hardcoding access keys in the backend block / config (leak city).
- Migrating state by copying files instead of init -migrate-state.
- Versioning off → a bad apply permanently taps the world (no rollback).
- One monolithic backend role = every repo can touch every env's state.
Debugging tips
- Lock error persists after a crash → inspect the lock row's LeaseID and release via CLI once confirmed.
- Backend config drift → ensure -backend-config matches across env pipelines (init re-runs hit the old lens).
- Plan can't read state → verify bucket+key+role per environment (typo in profile/etc.).
- Cloud run stuck 'queued' → VCS webhook/PRClosed issues; check agent connectivity.
Interview checkpoint
Quick quiz
Object versioning on the state bucket enables:
The lock table's purpose is: