Terraform State Management with Amazon S3

Published: (January 7, 2026 at 02:02 AM EST)
6 min read
Source: Dev.to

Source: Dev.to

Why Terraform State Matters

Terraform uses a state file (terraform.tfstate) to track the infrastructure it manages. This file contains the mappings between your configuration and real resources, allowing Terraform to make safe and predictable changes in future runs.

By default, Terraform stores state locally. That works for experimentation but is unsafe for collaboration, automation, and production use.

Why Use a Remote Backend like S3?

BenefitDescription
Centralized stateAccessible by all team members
Collaboration supportPrevents “state drift” between users
Encryption & IAMSecure access control
VersioningEasy recovery from accidental deletions or corruption
State lockingAvoids concurrent‑write conflicts

Terraform’s official documentation confirms that S3 is a supported backend type that can store state and support locking — it’s one of the most popular and reliable backends used in AWS environments.

State Locking — Preventing Conflicts

Terraform must prevent two people (or CI jobs) from writing changes to the same state file at the same time; otherwise the state could become corrupted.

Modern Locking (S3 Native)

Terraform now supports native state locking in S3 through a lock‑file mechanism. Terraform creates a .tflock file in the bucket alongside your state file, preventing concurrent runs.

terraform {
  backend "s3" {
    bucket       = "my-terraform-state-bucket2z"
    key          = "envs/prod/terraform.tfstate"
    region       = "us-east-1"
    use_lockfile = true
  }
}
  • use_lockfile = true enables S3‑native locking.
  • S3 creates a .tflock file during a Terraform run.
  • Other processes must wait until the lock is released.

This removes the need for a separate DynamoDB table for state locking — an approach that is officially deprecated and will be removed in future Terraform releases.

Note: DynamoDB locking is still supported for backward compatibility, but Terraform now prefers S3 native locking for new configurations.

Prerequisites

ToolCommand
AWS CLI (installed & configured)aws configure
Terraform (installed)terraform version
PermissionsAbility to create S3 buckets and apply Terraform changes

Step 1 — Create an S3 Bucket for Terraform State

Terraform cannot create the backend bucket for you, so you must create it first.

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

Replace my-terraform-state-bucket2z with a globally unique name.

Step 2 — Enable Bucket Versioning (Best Practice)

Versioning helps you recover from accidental deletions or corruption.

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

Step 3 — Enable Encryption on the Bucket

Encrypting state at rest is essential for security.

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

Troubleshooting: AccessDenied Error

If you encounter an AccessDenied error when running the encryption command:

An error occurred (AccessDenied) when calling the PutBucketEncryption operation: Access Denied

Common causes and fixes

IssueFix
Insufficient IAM permissionsAdd s3:PutEncryptionConfiguration to the IAM policy.
Bucket doesn’t existVerify the bucket name: aws s3api head-bucket --bucket my-terraform-state-bucket2z
Wrong AWS credentialsVerify your identity: aws sts get-caller-identity

Step 4 — Configure Terraform Backend

In your Terraform directory, define the backend like this:

terraform {
  backend "s3" {
    bucket       = "my-terraform-state-bucket2z"
    key          = "demo/terraform.tfstate"
    region       = "us-east-1"
    use_lockfile = true
  }
}
OptionDescription
bucketThe S3 bucket you created
keyPath (within the bucket) to store the state file
use_lockfileEnables native S3 state locking

Demo: Using S3 Remote State

1. Create a Terraform Project

mkdir terraform-s3-state-demo
cd terraform-s3-state-demo

2. Add Terraform Code

Create a file main.tf:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  backend "s3" {
    bucket       = "my-terraform-state-bucket2z"
    key          = "demo/terraform.tfstate"
    region       = "us-east-1"
    use_lockfile = true
  }
}

(Add the rest of your infrastructure resources below this block as needed.)

Run the usual Terraform workflow:

terraform init      # Initializes the backend and downloads providers
terraform plan      # Shows the execution plan
terraform apply      # Applies the changes and stores state in S3

The state file will be stored at s3://my-terraform-state-bucket2z/demo/terraform.tfstate, and Terraform will create a .tflock file during each run to prevent concurrent modifications.

Wrap‑up

Storing Terraform state in an Amazon S3 remote backend with native locking gives you:

  • Centralized, versioned, and encrypted state storage
  • Safe collaboration across teams
  • Automatic state locking without the overhead of a DynamoDB table

Apply these practices to any production Terraform workflow on AWS, and you’ll avoid many of the common pitfalls that lead to state corruption and deployment failures. Happy provisioning!

terraform {
  backend "s3" {
    bucket        = "my-terraform-state-bucket2z"
    key           = "demo/terraform.tfstate"
    region        = "us-east-1"
    encrypt       = true
    use_lockfile  = true
  }
}

provider "aws" {
  region = "us-east-1"
}

resource "aws_s3_bucket" "example" {
  bucket = "tf-s3-backend-demo-example"
}

Initialize the Backend

terraform init

Terraform will initialize and automatically configure the backend.

Apply the Changes

terraform apply

Your state file is now stored in S3, and state locking is enabled.

S3 remote state example

Useful Terraform Backend Commands

Migrate Local State to Remote

terraform init -migrate-state

Reconfigure Backend

terraform init -reconfigure

List State Resources

terraform state list

Inspect a Resource

terraform state show aws_s3_bucket.example

Additional Best Practices

Store State Remotely

Use S3 or Terraform Cloud — never store state locally in team environments.

Don’t Edit or Delete State Manually

Manual changes corrupt state.

Isolate State by Environment

Use different keys for different environments:

envs/dev/terraform.tfstate
envs/staging/terraform.tfstate
envs/prod/terraform.tfstate

Regular Backups

Enable S3 versioning so you can roll back a state that was accidentally overwritten or deleted.

Lock State on All Writes

Enable use_lockfile = true. It ensures only one Terraform process can modify state at a time, protecting against conflicts and corruption.

Cleanup: Removing Demo Resources Safely

Step 1: Destroy Terraform Resources

terraform destroy

Terraform destroy output

  • This command will:
    • Read the state file from S3
    • Identify all managed resources
    • Safely delete them from AWS

After confirming, all demo infrastructure is removed, but the Terraform state file still exists in S3 (this is expected).

Step 2: Verify State File in S3 (Optional)

aws s3 ls s3://my-terraform-state-bucket2z/demo/

Terraform keeps the state file so you retain:

  • A record of managed infrastructure
  • Historical versions (if versioning is enabled)

Step 3: Delete the Terraform State File (Optional)

aws s3 rm s3://my-terraform-state-bucket2z/demo/terraform.tfstate

Warning: Never delete a state file while infrastructure still exists — this will orphan resources.

Step 4: (Optional) Disable or Remove Lock Files

If you used S3 native locking, Terraform automatically removes the .tflock file after operations complete. Manual removal is normally unnecessary.

Step 5: Delete the S3 Backend Bucket (Optional)

If the bucket was created solely for this demo:

Empty the bucket

aws s3 rm s3://my-terraform-state-bucket2z --recursive

Delete the bucket

aws s3api delete-bucket \
  --bucket my-terraform-state-bucket2z \
  --region us-east-1

Cleanup Best Practices

  • Always run terraform destroy first.
  • Never delete state files before destroying resources.
  • Keep state buckets for long‑term projects.
  • Enable versioning for recovery.
  • Restrict access using IAM policies.

Summary

Terraform state is foundational to infrastructure automation. Storing it in a remote S3 backend with native state locking:

  • Enables safe collaboration
  • Prevents concurrent modifications
  • Allows controlled recovery
  • Supports CI/CD workflows

S3 remote state with native locking is now a best practice recommended by Terraform — and removes the need for a separate DynamoDB lock table in most cases.

References

Final Thoughts

Terraform state cleanup is just as important as provisioning.

A safe cleanup process ensures:

  • No orphaned resources
  • No unexpected AWS charges
  • Clean environments for future work

When using remote state: Terraform is always the source of truth — let it manage the lifecycle from start to finish.

Back to Blog

Related posts

Read more »

Rapg: TUI-based Secret Manager

We've all been there. You join a new project, and the first thing you hear is: > 'Check the pinned message in Slack for the .env file.' Or you have several .env...

Technology is an Enabler, not a Saviour

Why clarity of thinking matters more than the tools you use Technology is often treated as a magic switch—flip it on, and everything improves. New software, pl...