Terraform Backend
A Terraform backend defines where Terraform stores its state file and how operations like plan and apply are executed. The default backend stores state locally on disk — production teams use remote backends like S3 or Terraform Cloud to share state safely across a team.
What Is a Terraform Backend?
The backend answers two questions: where does the state file live, and where does Terraform run its operations? By default, state lives on your laptop in terraform.tfstate. The moment a second engineer joins your team, that stops working — you need a remote backend that everyone can access, with locking to prevent concurrent applies.
Think of the backend as the shared filing cabinet for your team's infrastructure records. The local backend is your personal desk drawer — fine when you work alone, unusable for a team.
Local Backend (Default)
# No backend block = local backend# State stored at ./terraform.tfstate# Fine for learning, bad for teamsS3 Remote Backend (Standard for AWS Teams)
# versions.tfterraform { backend "s3" { bucket = "razorpay-terraform-state" # S3 bucket for state key = "prod/api-service/terraform.tfstate" # path within bucket region = "ap-south-1" encrypt = true # SSE encryption for state dynamodb_table = "terraform-state-lock" # DynamoDB table for locking }}Setting Up the S3 Backend Prerequisites
# Create S3 bucket with versioningaws s3api create-bucket \ --bucket razorpay-terraform-state \ --region ap-south-1 \ --create-bucket-configuration LocationConstraint=ap-south-1 # Enable versioning so you can recover old stateaws s3api put-bucket-versioning \ --bucket razorpay-terraform-state \ --versioning-configuration Status=Enabled # Create DynamoDB table for state lockingaws dynamodb create-table \ --table-name terraform-state-lock \ --attribute-definitions AttributeName=LockID,AttributeType=S \ --key-schema AttributeName=LockID,KeyType=HASH \ --billing-mode PAY_PER_REQUEST \ --region ap-south-1Initialising a Backend
# After adding or changing the backend block:terraform init # Terraform will ask: "Do you want to copy existing state to the new backend?"# Answer yes to migrate local state to S3 # If you change backend config without changing the backend type:terraform init -reconfigure # Migrate state from one backend to another:terraform init -migrate-statePartial Backend Configuration
Hardcoding bucket names in the backend block makes it hard to reuse across environments. Use partial config:
# versions.tf — only the backend type, no valuesterraform { backend "s3" {}}# Pass values at init time — different per environmentterraform init \ -backend-config="bucket=razorpay-terraform-state" \ -backend-config="key=prod/payments/terraform.tfstate" \ -backend-config="region=ap-south-1" \ -backend-config="dynamodb_table=terraform-state-lock"Terraform Cloud Backend
terraform { cloud { organization = "razorpay" workspaces { name = "payments-prod" } }}Backend Comparison
| Backend | State Storage | Locking | Best For |
|---|---|---|---|
| local | Local disk | None | Solo learning only |
| s3 | AWS S3 | DynamoDB | AWS teams |
| gcs | GCP Cloud Storage | Built-in | GCP teams |
| azurerm | Azure Blob | Built-in | Azure teams |
| Terraform Cloud | Managed | Built-in | Any team, managed |
SecurityRestrict who has access to the S3 state bucket. The state file contains plaintext sensitive values. Use a bucket policy that allows only the CI/CD role and senior engineers to read the bucket directly.
Common MistakePutting the backend S3 bucket and DynamoDB table inside the same Terraform configuration they are used for. You cannot use Terraform to create the bucket that stores its own state. Create these resources manually or with a separate bootstrap configuration.
Frequently Asked Questions
Why is local state considered dangerous for anything beyond solo experimentation?
Local backend state lives as a plaintext JSON file on one engineer's disk with no locking, no encryption at rest by default, and often containing sensitive values (like database passwords) in plain text — if that laptop is lost, or two people run apply from different local copies, state gets lost or corrupted with no coordination mechanism. Remote backends like S3 (with DynamoDB locking) or Terraform Cloud solve locking, versioning, and shared access in one move.
What's a common mistake teams make when migrating from local to remote state?
Not backing up the local `terraform.tfstate` file before running `terraform init -migrate-state`, or having two engineers migrate to different remote backends simultaneously, causing a split-brain where Terraform's record diverges from reality. Also worth knowing: switching backends requires re-running `terraform init`, and state files often contain secrets in plaintext, so the remote backend needs its own access controls and encryption, not just locking.