Skip to main content
This lesson shows how to configure Terraform to store its state remotely in Amazon S3. Using a remote backend provides centralized state storage, versioning, and — when paired with DynamoDB — safe state locking for team workflows or automation. If you prefer a different backend, consult the Terraform backend docs for that provider: https://developer.hashicorp.com/terraform/language/settings/backends Prerequisites:
  • An AWS account with permissions to manage S3, DynamoDB, and KMS (if you enable KMS).
  • Terraform installed locally.
  • Credentials available to Terraform (environment variables, AWS CLI profile, or an assumed role).
Below is a small existing Terraform configuration we will manage with a remote backend. Keep this file (for example main.tf) in your working directory.
Overview of the steps
  1. Create an S3 bucket to hold the state (enable versioning and encryption).
  2. Optionally create a DynamoDB table for state locking.
  3. Add an S3 backend block to your Terraform configuration.
  4. Run terraform init to initialize the remote backend.
  5. Apply your configuration; Terraform will store state in S3.
Step 1 — Create the S3 bucket Create an S3 bucket to store Terraform state. Recommended settings:
  • Bucket versioning enabled (for state history and rollbacks).
  • Server-side encryption enabled (SSE-S3 or SSE-KMS). SSE-KMS is recommended when you need fine-grained key control and auditing.
  • Block public access enabled.
In my example I created a bucket named krausen-terraform-state-bucket in us-east-2, enabled versioning, and enabled server-side encryption with KMS and the bucket key.
Screenshot of the Amazon S3 "Create bucket" console showing default encryption settings. Dual-layer server-side encryption (DSSE-KMS) is selected with a chosen AWS KMS key ARN and the bucket key enabled.
After creation I copied the bucket name for the backend configuration.
A screenshot of the Amazon S3 Buckets console with a green banner confirming the successful creation of the bucket "krausen-terraform-state-bucket." The General purpose buckets list shows three buckets and S3 actions like Create bucket, Copy ARN, Empty, and Delete.
Optional: create a DynamoDB table for locking To avoid concurrent state writes, create a DynamoDB table with a primary key named LockID (string). Terraform uses this table to acquire locks while modifying state. Example AWS CLI command to create the table:
Step 2 — Add the S3 backend configuration Create a file named terraform.tf (or add to an existing .tf file) and add a terraform block that configures the s3 backend. Provide the bucket name, an object key (path inside the bucket), the region, and optionally the dynamodb_table for locking and encrypt/kms_key_id options.
Backend configuration reference Step 3 — Initialize the backend After saving the backend block, run terraform init in your working directory. Terraform will configure the backend, download provider plugins, and create the lock file .terraform.lock.hcl. Example terraform init output:
You must authenticate Terraform so it can access the S3 bucket (and the DynamoDB table, if used). Options include environment variables, a credential/profile configured in the AWS CLI, or an assumed role. Choose the authentication method that fits your environment and ensure credentials are available before running terraform init or terraform apply.
Tip: run terraform fmt to format your .tf files before applying. Step 4 — Apply your configuration When you run terraform apply (for example with -auto-approve), Terraform creates resources in your AWS account and writes the state file to the configured S3 key. Example apply output:
You will not find a local terraform.tfstate file — the state is stored remotely at prd/terraform.tfstate inside krausen-terraform-state-bucket. With S3 versioning enabled, each state update becomes a new version; you can use S3 versioning to roll back to previous state versions if necessary. Best practices summary
  • Use a dedicated, versioned, and encrypted S3 bucket for Terraform state.
  • Use DynamoDB for state locking when multiple users or CI pipelines interact with the same state.
  • Restrict access to the state bucket and KMS key using IAM policies.
  • Keep .terraform.lock.hcl in version control, but do not check in sensitive state data (remote backends keep this out of your repo).
  • Consider separating environments via different state keys or buckets (e.g., dev/terraform.tfstate, prd/terraform.tfstate).
Links and references

Watch Video