TrademarkTrademark
Features
Documentation

Terraform State Files Best Practices

Learn what Terraform state is, best practices to use with state, and how to manipulate it.
Ryan FeeMarch 4, 2026Updated March 31, 2026
Key takeaways
  • Terraform state is the source of truth that maps your configuration to real infrastructure, storing metadata, outputs, resource IDs, attributes, and dependencies in a JSON file you must never edit manually.
  • Remote state is essential for any team or production use because it centralizes storage, enables state locking, provides versioning for rollbacks, and avoids exposing sensitive data on local machines.
  • Best practices include using a remote backend, enabling state versioning, separating state per environment, and keeping secrets out of state by marking outputs sensitive and using external secret managers.
  • Terraform state commands like list, show, mv, rm, and import let you inspect and modify state safely, with terraform state rm removing a resource from state without destroying the real infrastructure.
  • Reduce blast radius and improve performance by breaking infrastructure into smaller modular components with separate state files, and recover from corruption by restoring a versioned backup.

Terraform state is the record of what Terraform manages in the real world. It's the source of truth Terraform checks before it plans or applies a change, so without it Terraform can't tell what already exists. Beginners tend to treat state as an implementation detail they can ignore, and that's usually where the trouble starts. Managing it well is what keeps your deployments predictable.

What is Terraform State?

Terraform state maps your configuration to the actual resources it manages. It tracks metadata, resource IDs, attributes, and dependencies so Terraform can understand how resources relate and apply updates in the right order. The data is stored as JSON, in a file named terraform.tfstate by default. That file also holds your outputs, and it can contain sensitive data if you aren't careful. Never edit it by hand. Knowing what's inside it and why is fundamental to using Terraform well.

State File Components

Here's a breakdown of its key components:

  • Metadata: The state file begins with metadata about the state format itself and the Terraform version that last updated it. This helps Terraform understand how to read and interpret the file and ensures compatibility.
  • Outputs: Any outputs you define in your Terraform configuration (e.g., output "instance_ip" { value = aws_instance.web.public_ip }) are stored here. These values can then be easily accessed by other Terraform configurations or external tools. Be cautious, as sensitive outputs (like passwords) will be stored in plain text unless explicitly marked as sensitive.
  • Resources Array: This is the most crucial part of the state file. It contains a list of all resources that Terraform is currently managing. For each resource, you'll find:
    • Resource Type and Name: A clear identifier linking to your Terraform configuration (e.g., aws_instance.web).
    • Provider Information: Details about the Terraform provider used to manage the resource (e.g., provider["registry.terraform.io/hashicorp/aws"]).
    • Instance Details: Each resource instance (if there are multiple) will have its own entry. This includes:
      • Unique ID: The actual ID of the resource as assigned by the cloud provider (e.g., i-0abcdef1234567890 for an AWS EC2 instance). This is how Terraform links its configuration to the real-world object.
      • Attributes: All the attributes of the resource, including those you defined in your configuration and those automatically assigned by the provider (e.g., public IP, ARN, security group IDs, instance state). These attributes represent the current known state of the resource.
      • Dependencies: Implicit or explicit dependencies between resources. This allows Terraform to understand the order in which resources must be created, updated, or destroyed.

Local vs. Remote State: Remote is King

Local state causes real problems once more than one person is involved. You hit merge conflicts, the file is easy to delete or corrupt by accident, there's no versioning to fall back on, and sensitive data ends up sitting on individual laptops. A remote backend fixes all of that. It keeps state in one shared place, locks it so two people can't write at once, versions it so you can roll back, and stores it somewhere more durable and secure than a developer's machine. For any team or production workload, remote state is essential.

Choosing a Remote Backend

Several remote backends are available for Terraform state. Popular choices include AWS S3, Azure Blob Storage, Google Cloud Storage, Scalr, and Terraform Cloud/Enterprise.

Here’s how you might configure some:

AWS S3 Backend Example:

terraform {
  backend "s3" {
    bucket         = "my-terraform-state-bucket"
    key            = "path/to/my/infra.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-lock-table"
    encrypt        = true
  }
}

Scalr Example:

terraform {
  backend "remote" {
    hostname = "<account-name>.scalr.io"
    organization = "<scalr-environment-name>"

    workspaces {
      name = "<workspace-name>"
    }
  }
}

When you pick a backend, weigh cost against the cloud provider you already use, how familiar your team is with the service, and whether you need extras like policy as code or richer collaboration features.

Best Practices

A few habits keep state management from going sideways. Here are the ones we lean on most:

  • Always use a remote backend: Centralize your state (e.g., AWS S3 with DynamoDB, Azure Blob Storage, Terraform Cloud) for team collaboration, state locking, and durability.
  • Enable state file versioning: Allow for rollbacks and an audit trail of all state changes.
  • Avoid manually editing the state file: Rely solely on Terraform's terraform state commands to avoid corruption.
  • Separate state files: Isolate state for different environments (dev, staging, prod) or logical components to reduce error impact.
  • Avoid sensitive data in state: Do not store secrets directly in the state file; use the sensitive attribute for outputs and integrate with external secret managers.

State Manipulation Commands with Examples

Terraform ships commands for working with the state file directly. They let you inspect and modify the resources Terraform tracks without ever opening the JSON yourself. Go slowly here: these commands change what Terraform believes it manages, and a wrong move is hard to undo.

**terraform refresh**: Updates the state file with the latest attributes from the real-world infrastructure. While terraform plan implicitly performs a refresh, running it explicitly can be useful to see if any drift has occurred before planning changes.

terraform refresh

terraform state rm <resource_address>: Removes a resource from the state file. This does not destroy the actual infrastructure resource. Use this with extreme caution when you want Terraform to "forget" about a resource it no longer manages, perhaps because it's now managed manually or by another process.

terraform state rm aws_instance.web

terraform state mv <source_address> <destination_address>: Moves a resource within the state. This is useful when refactoring your Terraform configuration, such as moving a resource into a module.

# Before: aws_instance.old_name
# After: module.web_server.aws_instance.new_name
terraform state mv 'aws_instance.old_name' 'module.web_server.aws_instance.new_name'

terraform state show <resource_address>: Displays the attributes of a specific resource as recorded in the state.

terraform state show aws_instance.web

Example Output (partial):

# aws_instance.web:
resource "aws_instance" "web" {
    id                          = "i-0abcdef1234567890"
    ami                         = "ami-0abcdef1234567890"
    instance_type               = "t2.micro"
    // ... other attributes
}

terraform state list: Shows a list of all resources tracked in the current state file.

terraform state list

Example Output:

aws_instance.web
aws_vpc.main

terraform import: Imports an existing resource into the state file. For the full workflow, including the modern import {} block, generating Terraform code from existing resources, and bulk-import patterns, see the Terraform import guide.

terraform import aws_instance.example i-abcd1234

Each of these gives you a controlled way to change state, which is far safer than editing the file by hand.

Advanced State Management: Workspaces and Isolation

As environments get more complex, how you isolate state starts to matter. Terraform Workspaces can split environments like dev and staging within a single configuration, using commands such as terraform workspace new <name> and terraform workspace select <name>. They're convenient, but they don't give you much separation. For real isolation and a smaller blast radius, separate directories with their own remote backend per environment or component is usually the safer choice.

To share information between different state files, use the terraform_remote_state data source. This allows one configuration to read outputs from another, facilitating modular and interconnected infrastructure deployments.

data "terraform_remote_state" "network" {
  backend = "s3"
  config = {
    bucket = "my-network-state-bucket"
    key    = "network.tfstate"
    region = "us-east-1"
  }
}

resource "aws_instance" "web" {
  subnet_id = data.terraform_remote_state.network.outputs.web_subnet_id
  # ...
}

Common Pitfalls and Troubleshooting

Even with good habits, things go wrong. State can get corrupted by a network blip, a hand edit, or a process that dies mid-run. Recovery usually means restoring a versioned backup, and manual repair only when you have no other option. State locking handles most concurrency problems before they start.

Drift detection matters too: terraform plan shows you where your state and the real infrastructure have diverged. To keep secrets out of state, mark outputs with the sensitive attribute and lean on external secret managers rather than storing them inline. Large state files also drag down performance, so break big configurations into smaller modular components, each with its own state file. The drift detection guide goes deeper if you want it.

This blog has been verified for Terraform and OpenTofu

About the author
Ryan Feedirector of platform engineering at Scalr
Ryan Fee is the director of platform engineering at Scalr, with over 15 years of experience improving infrastructure experiences at companies large and small.