← Back to Blog RSS

What Is a Terraform State File and Where Is It Stored?

Josh Pollara October 7th, 2025
TL;DR
$ cat terraform-state.tldr
• State = JSON map from config to real infrastructure
• Default: ./terraform.tfstate (breaks with teams)
• Remote backends (S3/Azure/GCS) enable collaboration + locking
• Migrate: add backend block → terraform init -migrate-state

If you're working with Terraform, understanding where state is stored and how to manage it properly is the difference between smooth infrastructure deployments and late-night debugging sessions trying to figure out why resources disappeared.

The Terraform state file is the single source of truth for your infrastructure. It's what allows Terraform to know what already exists, what needs to change, and how resources depend on each other. But where does this critical file live, and when should you move it?

This guide breaks down everything you need to know about Terraform state files and storage, from the basics to production-ready configurations, with visual examples to make it crystal clear.

What Is a Terraform State File?

A Terraform state file is a JSON file named terraform.tfstate that records the last known state of your infrastructure as Terraform understands it. Think of it as Terraform's memory of what exists in your cloud environment.

When you write Terraform configuration like this:

resource "aws_instance" "web_server" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}

Terraform needs a way to map this code to the actual EC2 instance that gets created in AWS. The state file stores the connection between your configuration and the real resource ID (like i-0123456789abcdef0).

Why State Files Matter

Without state, Terraform has no way to know what infrastructure already exists. Every terraform apply would try to create everything from scratch, resulting in duplicate resources and errors.

The state file contains several key pieces of information:

Resource IDs: The unique identifiers assigned by your cloud provider (like i-0123456789abcdef0 for an EC2 instance)

Resource attributes: Current configuration values like IP addresses, sizes, and tags

Dependencies: Which resources depend on others, so Terraform knows the correct order for creating or destroying them

Outputs: Values that other Terraform configurations might reference

Here's a simplified example of what's inside a state file:

{
"version": 4,
"resources": [
{
"type": "aws_instance",
"name": "web_server",
"instances": [{
"attributes": {
"id": "i-0123456789abcdef0",
"ami": "ami-0c55b159cbfafe1f0",
"instance_type": "t2.micro",
"public_ip": "54.123.45.67"
}
}]
}
]
}

Where Does Terraform Store State by Default?

When you run terraform apply for the first time, Terraform stores state in a local file on your machine. The default location is:

$ ls -la
-rw-r--r-- 1 user user 4523 Oct 7 10:15 terraform.tfstate
-rw-r--r-- 1 user user 4201 Oct 7 09:42 terraform.tfstate.backup

This is called the local backend. The state file lives right in your working directory, wherever you ran terraform init and terraform apply.

Terraform also creates a backup file called terraform.tfstate.backup that contains the previous version of your state. This gives you one level of rollback protection if something goes wrong.

Important Note

Never manually edit the state file. Use Terraform commands like terraform state to make any modifications. Manual edits can corrupt the file and break your infrastructure management.

The Problem with Local State

Local state works fine when you're the only person working on a Terraform project. But the moment you add teammates or want to run Terraform from CI/CD, local state becomes a major problem.

No Collaboration

If the state file lives on your laptop, how do your teammates know what infrastructure exists? They can't run Terraform because they don't have your state file. You end up being the only person who can make infrastructure changes.

$ terraform plan
Error: No state file found!
Terraform will attempt to create all resources from scratch...
Error: Resource already exists

No Locking

What happens if two people somehow share the state file (maybe via Dropbox or Git) and both run terraform apply at the same time? Chaos. Both processes read the same state, make different changes, and write back their versions. One person's changes get silently overwritten.

No Versioning

Local state keeps exactly one backup. If you need to roll back multiple changes or review state history, you're out of luck. There's no audit trail of who changed what and when.

No Security

The state file contains sensitive information like resource IDs, IP addresses, and sometimes passwords or API keys. A local file on your laptop is easily exposed if you accidentally commit it to Git or share it insecurely.

Rule of Thumb

Local state is acceptable only for personal learning projects or temporary experiments. For any team environment or production infrastructure, you need remote state.

Remote State: Where Terraform Should Store State

Remote state solves all the problems of local state by storing the state file in a shared, secure backend that everyone on your team can access. The three most common remote backends are:

AWS S3
s3://bucket/state.tfstate
Azure Blob Storage
azurerm://container/state
Google Cloud Storage
gs://bucket/state.tfstate

When you configure a remote backend, Terraform stores state in cloud storage instead of on your local machine. Every team member and CI/CD pipeline reads and writes to the same central state file.

Benefits of Remote State

Team collaboration: Everyone works from the same state. No more "it works on my machine" problems.

State locking: Remote backends prevent concurrent modifications. If Alice is running terraform apply, Bob's attempt will wait until Alice finishes.

Versioning: Cloud storage providers offer version history. You can see every state change and roll back if needed.

Encryption: State is encrypted at rest and in transit, protecting sensitive data.

Access control: Use cloud IAM to control who can read or modify state.

How to Configure Remote State

Configuring remote state is straightforward. You add a backend block to your Terraform configuration and run terraform init to migrate. Here's how to do it for each major cloud provider.

S3 Backend (AWS)

First, create an S3 bucket to store your state. Enable versioning on the bucket for state history. Then add this backend configuration:

terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true
use_lockfile = true # Built-in S3 locking (Terraform 1.5+)
}
}

The bucket is where your state lives. The key is the path within that bucket. Use encrypt = true to ensure state is encrypted at rest. For Terraform 1.5+, use_lockfile = true enables native S3 locking without needing DynamoDB.

Azure Blob Storage Backend

Create an Azure Storage Account and container for state storage. Azure automatically handles locking via blob leases.

terraform {
backend "azurerm" {
resource_group_name = "rg-terraform-state"
storage_account_name = "tfstateaccount"
container_name = "tfstate"
key = "prod.tfstate"
}
}

Azure Storage encrypts data by default and provides automatic locking. Configure access via Azure RBAC or managed identities instead of hardcoding credentials.

Google Cloud Storage Backend

Create a GCS bucket with versioning enabled. GCS handles locking automatically using object generation numbers.

terraform {
backend "gcs" {
bucket = "my-terraform-state"
prefix = "terraform/state/prod"
}
}

GCS encrypts state at rest by default. Use Cloud IAM to control access and provide credentials via environment variables rather than hardcoding them.

Migrating from Local to Remote State

Once you add a backend block, migrate your existing local state to the remote backend:

$ terraform init -migrate-state
Initializing the backend...
Terraform detected that the backend type changed from "local" to "s3".
Do you want to copy existing state to the new backend? (yes/no)
> yes
Successfully configured the backend "s3"!
State successfully migrated to remote backend.

Terraform detects the backend change, copies your local state to the remote location, and confirms the migration. After this, you can safely delete your local terraform.tfstate file. The remote backend is now your source of truth.

Understanding Backend Configuration Options

When configuring a backend, there are several important decisions to make that affect how your state is stored and accessed.

State File Path and Organization

The key or prefix parameter determines where your state file lives within the storage bucket. Use a clear naming scheme that reflects your environments and projects:

# Organize by environment
key = "dev/terraform.tfstate"
key = "staging/terraform.tfstate"
key = "prod/terraform.tfstate"
# Organize by project and environment
key = "webapp/prod/terraform.tfstate"
key = "database/prod/terraform.tfstate"
key = "networking/prod/terraform.tfstate"

Separate state files per environment prevent accidental cross-environment changes. If you're deploying a development change, you can't accidentally affect production because the state files are completely isolated.

Encryption and Security

Always enable encryption for state files. They contain sensitive resource information and sometimes secrets.

For S3, use encrypt = true to enable server-side encryption. For additional security, specify a KMS key:

terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true
kms_key_id = "arn:aws:kms:us-east-1:123456789:key/abc-def"
use_lockfile = true
}
}

Restrict access to the state storage using cloud IAM policies. Only Terraform users and CI/CD systems should have read/write access. Never make state buckets publicly accessible.

Security Best Practice

Never commit state files to Git. Add *.tfstate and *.tfstate.backup to your .gitignore immediately. State files often contain secrets and should only exist in secure remote backends.

State Locking Configuration

State locking prevents concurrent modifications that could corrupt your state. Most remote backends handle locking automatically:

S3 (Terraform 1.5+): Use use_lockfile = true for native S3 locking. For older versions, configure a DynamoDB table with dynamodb_table = "terraform-lock".

Azure: Locking happens automatically via blob leases. No additional configuration needed.

GCS: Locking is automatic using object generation numbers and preconditions.

When locking is active, you'll see messages like this if someone else is making changes:

$ terraform apply
Acquiring state lock. This may take a few moments...
Waiting for state lock...
Lock acquired successfully.

Common Questions About State Storage

Can I use the same backend for multiple projects?

Yes, but use different state file paths (keys) for each project. One bucket can hold many state files, organized by path:

my-terraform-state/
├── project-a/
│ ├── dev/terraform.tfstate
│ └── prod/terraform.tfstate
├── project-b/
│ ├── dev/terraform.tfstate
│ └── prod/terraform.tfstate
└── networking/terraform.tfstate

What happens if I lose my state file?

If you're using remote state with versioning enabled, you can recover previous versions from your cloud storage provider's version history. This is why versioning is critical—it's your safety net.

If you lose state entirely with no backups, you'll need to rebuild it by importing existing resources using terraform import. This is tedious and error-prone, which is why proper state management matters.

Should I commit backend configuration to Git?

Yes, commit the backend configuration block itself (the code in backend.tf). This ensures everyone on your team uses the same remote backend. Never commit the actual state file (terraform.tfstate), only the configuration that points to where it's stored.

Can I change backends after setting one up?

Yes. Update your backend configuration and run terraform init -migrate-state again. Terraform will copy state from the old backend to the new one. This lets you switch from S3 to GCS, or consolidate multiple backends, without losing your state.

Do I need different backends for different environments?

Not necessarily. You can use the same backend (like one S3 bucket) but with different state file paths for each environment. The key is ensuring each environment has its own isolated state file so changes don't cross environments.

When to Use Local vs Remote State

Here's a simple decision tree for choosing your state storage:

Use Local State If:
You're learning Terraform for the first time
You're doing a quick experiment or prototype
You're the only person who will ever touch this code
The infrastructure is temporary (will be destroyed soon)
Use Remote State If:
Multiple people work on the same infrastructure
You're running Terraform from CI/CD
The infrastructure is production or long-lived
You need version history and audit trails
You need to protect against state corruption or loss

For almost any real-world scenario, remote state is the right choice. The setup overhead is minimal compared to the problems it prevents.

Quick Start: Migrating to Remote State

Here's a step-by-step checklist for moving from local to remote state:

# 1. Create your remote storage (S3 bucket, Azure container, or GCS bucket)
# 2. Enable versioning on the storage for backup protection
# 3. Add backend configuration to your Terraform code
# 4. Run migration command
$ terraform init -migrate-state
# 5. Confirm the migration when prompted
# 6. Verify state is in remote backend
$ terraform state list
# 7. Delete local state files
$ rm terraform.tfstate terraform.tfstate.backup
# 8. Add *.tfstate to .gitignore if not already present

After migration, your team accesses the same state automatically. No manual file sharing required. Terraform handles all the backend communication transparently.

The Bottom Line

The Terraform state file is your infrastructure's source of truth. It maps your code to real cloud resources, tracks dependencies, and enables Terraform to determine what needs to change.

By default, Terraform stores state locally in terraform.tfstate in your working directory. This works for solo experimentation but fails immediately when you add teammates, CI/CD, or need any kind of collaboration.

Remote backends solve this by storing state in cloud storage with automatic locking, versioning, encryption, and access control. Migrating from local to remote state takes minutes and prevents countless hours of debugging corrupted or lost state.

Key Takeaway

If more than one person or system touches your Terraform code, use remote state. The small upfront setup cost pays for itself the first time it prevents a state corruption disaster.

State management that eliminates lock contention

Stategraph uses resource-level locking instead of file-level locking.
Your team works in parallel without waiting for locks.

Get Updates Become a Design Partner

// Updates on Stategraph development, no spam