Terraform Tutorial — S3 Backend Setup

1 minute read

The S3 backend is the most popular choice for AWS teams. It stores state in an S3 bucket with optional encryption and versioning.


Step 1: Create the S3 Bucket

Create the bucket manually or with a separate Terraform config (bootstrap):

$ aws s3api create-bucket \
    --bucket my-company-terraform-state \
    --region us-east-1

Enable versioning:

$ aws s3api put-bucket-versioning \
    --bucket my-company-terraform-state \
    --versioning-configuration Status=Enabled

Enable encryption:

$ aws s3api put-bucket-encryption \
    --bucket my-company-terraform-state \
    --server-side-encryption-configuration '{
      "Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "AES256"}}]
    }'

Step 2: Configure the Backend

# backend.tf

terraform {
  backend "s3" {
    bucket  = "my-company-terraform-state"
    key     = "projects/web-app/terraform.tfstate"
    region  = "us-east-1"
    encrypt = true
  }
}

Step 3: Initialize

$ terraform init

Initializing the backend...

Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.

Key Organization

Organize state keys by project and environment:

my-company-terraform-state/
├── projects/
│   ├── web-app/
│   │   └── terraform.tfstate
│   ├── api-service/
│   │   └── terraform.tfstate
│   └── shared-infra/
│       └── terraform.tfstate
└── environments/
    ├── dev/terraform.tfstate
    ├── staging/terraform.tfstate
    └── prod/terraform.tfstate

Best Practices

Practice Why
Enable versioning Recover from state corruption
Enable encryption Protect sensitive data
Block public access State contains secrets
Use a dedicated bucket Separate from application data
Use IAM policies Restrict who can read/write state

What’s Next?

Add DynamoDB locking to prevent concurrent modifications.


← Local vs Remote Backends DynamoDB Locking →