Terraform state explained: the file, backends and locking

By LearnSome.tech · Published 2026-11-04

TL;DR

Terraform state stores the bindings between objects in a remote system and the resource instances declared in your configuration. By default it lives in a local file named terraform.tfstate, and HashiCorp recommends storing it in HCP Terraform or a remote backend instead. If supported by your backend, Terraform locks your state for all operations that could write it.

What is the Terraform state file and why is it needed?

Terraform uses state to map real world resources to your configuration, keep track of metadata and improve performance for large infrastructures. When Terraform creates a remote object, it records the identity of that object against a particular resource instance. Terraform also keeps a copy of the most recent set of dependencies in state, so it can still work out the order for destruction after you delete items from the configuration. Before any operation, Terraform does a refresh to update the state with the real infrastructure.

Here is what that binding looks like from the command line, with comments on what each part tells you.

# Every resource Terraform manages has an address in state
$ terraform state list
aws_s3_bucket.logs
aws_instance.web

# One entry joins the address in your code to a real object
$ terraform state show aws_instance.web
# resource "aws_instance" "web" {
#     id            = "i-0abc..."   <- the real instance this address owns
#     instance_type = "t3.micro"    <- cached attribute values
#     ...
# }
# Delete the state and Terraform no longer knows i-0abc... is yours:
# the next apply tries to create a second instance.

Where should Terraform state live?

Storing state locally needs no extra configuration, but it limits collaboration and risks losing state if the local file is lost. With remote state, Terraform writes the state data to a remote data store that every member of a team can share. Remote state is implemented by a backend or by HCP Terraform, both of which you can configure in your configuration's root module. HashiCorp advises against storing state in a version control system or other storage that does not support state locking and secure access control, because it can result in data loss or exposure of secrets.

Here is a typical remote backend on Amazon S3, with comments on each setting.

terraform {
  backend "s3" {
    bucket       = "acme-terraform-state"     # an existing, private, versioned bucket
    key          = "network/terraform.tfstate" # one path per configuration
    region       = "eu-west-2"
    encrypt      = true                        # server side encryption of state and lock files
    use_lockfile = true                        # S3-native locking (a .tflock object)
  }
}
# Literal values only: no variables or locals in a backend block.
# Credentials come from the environment, never from this file.
# After adding or changing the block:
#   terraform init -migrate-state   copies existing state to the new backend

A backend block cannot refer to named values such as input variables, locals or data source attributes. The -migrate-state option of terraform init attempts to copy existing state to the new backend.

How does Terraform state locking work?

State locking happens automatically on all operations that could write state, and you see no message when it happens. If state locking fails, Terraform does not continue. Not all backends support locking, and each backend's documentation says whether it does. On the S3 backend, locking is opt-in and is enabled by setting the use_lockfile argument to true. DynamoDB-based locking on that backend is deprecated and will be removed in a future minor version.

Turn locking on before a second person or a CI job ever runs Terraform against the same state.

How do you fix a locked Terraform state?

The -lock-timeout option of terraform init overrides the time Terraform waits to acquire a state lock. Terraform has a force-unlock command to manually release the state lock if the automatic release failed. The command does not modify your infrastructure. HashiCorp says to be very careful with it, because releasing the lock while someone else is holding it could cause multiple writers. Use it only for your own lock when automatic release failed; it needs the unique lock ID that Terraform will output.

Before you force anything, find out who holds the lock and whether their run is still going.

# A teammate's apply is running: wait for the lock instead of failing
$ terraform plan -lock-timeout=5m

# Your own CI job crashed mid-apply and left its lock behind.
# Terraform printed the lock ID when it failed; check nobody else is running, then:
$ terraform force-unlock 6f1c2a9e-...      # the ID from the error, not a guess
# Never force-unlock a lock that belongs to a run that is still going.

Are secrets stored in Terraform state?

Yes: if you add secret values directly to your configuration, Terraform stores those secrets in its state and plan files. With local development, the state is a plaintext file that includes any secret values you defined in your configuration. HashiCorp's advice is to treat the state file as sensitive data and keep it out of Git workflows. Ephemeral values are available at runtime, but Terraform omits them from state and plan files entirely. Write-only arguments are only available during the current operation, and Terraform does not store them in state or plan files.

The sensitive argument on variable and output blocks redacts those values from Terraform CLI log output and the HCP Terraform UI.

How many state files should you have?

Some backends support multiple named workspaces, which associate several states with a single configuration. Workspaces are not appropriate for system decomposition or for deployments that need separate credentials and access controls. Remote state also lets teams share output values with other configurations, so infrastructure can be split into smaller components.

A useful rule of thumb is one state per thing you would change, review and roll back together. Keep production in its own state with its own credentials, so a mistake in a test environment can't reach it.

How should you inspect or change Terraform state?

Terraform stores state as a JSON text file, and HashiCorp says not to edit this file directly. You can use the terraform state commands to modify the Terraform state instead of modifying it directly. Every state subcommand that modifies the state writes a backup file. If you add or remove bindings by other means, such as terraform import or terraform state rm, you must keep the one-to-one mapping between resource instances and remote objects yourself.

Free official resources

One HashiCorp tutorial has you create an AWS instance and security group, examine your project's state file and remove infrastructure from your project's state.

From our labs

In our Terraform course, you show what goes wrong when state lives in a file on one laptop, from plain-text secrets to copies that diverge. You then name what a team's state storage must provide instead. A later lesson has you read a lock conflict, wait it out with a lock timeout, and judge when force-unlock is safe.

Start the free lesson: What Is Infrastructure As Code? (Infrastructure as Code with Terraform)

The course: Infrastructure as Code with Terraform

Terms in this guide

Sources

Written by the LearnSome.tech team with AI assistance; every fact checked against the cited source.