ENGINEERING NOTES / DEVOPS

Terraform State Management: Kill Local State

Why committing .tfstate to Git is a security disaster. A guide to setting up robust S3+DynamoDB remote backends with encryption and locking.

Terraform State Management: Kill Local State

Author: CloudOpsPro Engineering Team Category: DevOps, IaC, Security Date: December 12, 2025

We still see it in 2025: A terraform.tfstate file committed to a Git repository. This is the DevOps equivalent of storing your credit card number on a sticky note in a public park.

Terraform State is the “brain” of your infrastructure. If it is lost, corrupted, or exposed, it is Game Over.

1. Why Git Storage is Fatal

  • **Secrets in Plain Text: **Terraform state files store the results of resources. If you create an RDS database and pass a password, that password is stored in plain text in the state file. Commit to Git -> Secrets are compromised forever.
  • **No Locking: **Two engineers run terraform apply at the same time. Git doesn’t prevent this. They overwrite each other’s changes. Result: Corrupted infrastructure and undefined state.
  • **Manual Errors: **“Oops, I forgot to pull before applying.” Now your local state is out of sync with reality.

2. The Solution: Remote Backends with Locking

The extensive standard is S3 + DynamoDB (on AWS).

  • S3 Bucket: Stores the state file (Versioning enabled).
  • DynamoDB Table: Handles the Locking.

Configuration Example

# backend.tf

terraform {
  backend "s3" {
    bucket         = "company-terraform-state-prod"
    key            = "vpc/terraform.tfstate"
    region         = "us-east-1"

    # The Lock Table
    dynamodb_table = "terraform-lock-table"

    # Encryption works by default, but explicit is better
    encrypt        = true
  }
}

Now, when Engineer A runs terraform plan, Terraform writes a Lock ID to DynamoDB. If Engineer B tries to run apply, Terraform checks DynamoDB, sees the lock, and errors out: Error: Error acquiring the state lock.

Safety secured.

3. Best Practices for State Security

Since the state file contains secrets (RDS passwords, IAM keys, TLS private keys):

  1. Encryption at Rest: Ensure the S3 bucket has Server-Side Encryption (SSE-S3 or KMS) enabled.
  2. Access Control: Only the CI/CD role and top-level admins should have s3:GetObject on the state bucket. Developers generally don’t need raw state access; they just need to see the Plan output.
  3. Versioning: Enable S3 Versioning. If a terraform apply corrupts your state file, you can roll back the S3 object to the previous version to recover.

4. Terraform Cloud / Spacelift

If managing S3 backends feels too manual, managed backends like Terraform Cloud or Spacelift are excellent alternatives. They handle:

  • State hosting
  • Locking
  • History/Diffs of state
  • Run triggers

Verdict:

  • Small/Mid Teams: S3 + DynamoDB is fine (and free/cheap).
  • Large Enterprise: Use a managed backend for Audit logs and RBAC.

Whatever you do, add *.tfstate to your .gitignore right now.

A FRESH PERSPECTIVE ON YOUR CLOUD

Great engineering starts
with a good conversation.

Let’s talk about what’s working, what’s slowing you down, and what comes next.

Talk to an engineer ↗