Skip to main content
RunBook Academy

TerraformX · State Operations: Read, Move, Remove, ImportState

Local State: What It Looks Like and Why It Is Brittle

Intermediate⏱ ~14 min🧪 Lab requiredbashterraform

What you'll learn

  • Inspect a local state file safely
  • Recognise the structure of a state file
  • Identify the operational risks of local state in teams
  • Plan a migration to a remote backend

Prerequisites

Verified against Terraform CLI 1.9.x · OpenTofu 1.7.x · HCL 2.0 · bpg/proxmox provider 0.66+ · hashicorp/local provider 2.5+ · hashicorp/null provider 3.2+ · hashicorp/random provider 3.6+ · hashicorp/http provider 3.4+ · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · 2026-08-12

Not yet marked complete on this device.

Local state is the default. It is a JSON file on the working directorys disk. It is fine for one engineer on a disposable environment. It is not fine for a team operating production. This lesson teaches what local state looks like, why it is brittle, and how to migrate to a remote backend.

What local state looks like

When you run terraform apply for the first time, Terraform creates terraform.tfstate in the working directory:

ls -la terraform.tfstate
-rw-r--r-- 1 user user 4234 Aug 12 14:00 terraform.tfstate

The file is human-readable JSON:

{
  "version": 4,
  "terraform_version": "1.9.8",
  "serial": 17,
  "lineage": "a7c3f0e2-1234-5678-9abc-def012345678",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "aws_instance",
      "name": "web",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "schema_version": 1,
          "attributes": {
            "id": "i-0abc123def456789",
            "ami": "ami-0e1bed4f",
            "instance_type": "t3.medium",
            "tags": {
              "Name": "web-01"
            }
          }
        }
      ]
    }
  ]
}

The file has:

  • version — the state schema version.
  • terraform_version — the version that wrote the file.
  • serial — a monotonic counter; every write increments it.
  • lineage — a UUID that identifies the state lineage.
  • outputs — the computed outputs.
  • resources — one entry per resource, with the providers attributes.

The state does not contain:

  • The users .tf configuration.
  • The variable values that resolved during the last apply.
  • The history of past applies.

The state is a snapshot. It is not a journal.

Sensitive data in state

The state may contain secrets:

resource "aws_db_instance" "primary" {
  # ...
  password = var.db_password
}

The password attribute is in the state in plain text. The state is on disk. The disk is the encryption boundary.

For multi-tenant systems, the state may contain:

  • Database passwords.
  • TLS private keys.
  • API tokens.
  • OAuth client secrets.

The state is a database of secrets. The course has a dedicated lesson on state security (Part XXXI).

Why local state is brittle

A team of one engineer can use local state. A team of two or more cannot, for these reasons:

The state is on one engineers laptop. The existence of the state is bound to the existence of the working directory on one machine. If the laptop dies, the state is lost. If the engineer leaves the team, the state goes with them.

There is no locking. Two engineers can run terraform apply simultaneously. The first apply writes the state. The second apply reads the state from disk, makes its own changes, and writes the state. The two applies are not aware of each other.

There is no version history. The state file is overwritten on every apply. The previous state is gone. There is no audit trail.

There is no encryption. The state file is on disk in plain text. A backup of the laptop has the state. A shared file server has the state. The state is wherever the working directory is.

There is no concurrent access. Two engineers cannot work on the same configuration because there is no shared state.

The lifecycle of local state

Local state has a typical lifecycle:

  1. Day 1. The engineer creates the configuration. The state is terraform.tfstate on the laptop.
  2. Day 30. The engineer has 30 resources. The state is growing.
  3. Day 90. The engineer has 200 resources. The state is several megabytes.
  4. Day 365. The engineer has 1000 resources. The state is tens of megabytes. The laptop is slow.
  5. Day 400. The team grows to two engineers. They share the state via a file share. The file share corrupts the state.

This is the natural journey from local to remote state. The course has a dedicated runbook for the migration (Part XXXV).

Inspecting the state

The state has a small set of read-only commands:

# List every resource in the state
terraform state list

# Show the details of one resource
terraform state show aws_instance.web

# Show the entire state
terraform state show

The state shows the providers attributes. The state is the source of truth for what Terraform believes exists.

Backing up local state

Without a remote backend, the backup is your responsibility:

# Daily backup
cp terraform.tfstate terraform.tfstate.backup-$(date +%Y%m%d)

# Encrypted backup
gpg --symmetric --cipher-algo AES256 terraform.tfstate

# Offsite backup
rsync -avz terraform.tfstate.gpg backup@offsite:terraform/

The backup is the recovery. A backup that has never been restored is not a backup.

Migrating to a remote backend

The migration is a one-time terraform init -migrate-state operation:

# 1. Update the backend configuration
vim versions.tf

# 2. Run the migration
terraform init -migrate-state

# 3. Verify the state is in the new backend
terraform state list

The init:

  • Reads the new backend configuration.
  • Detects the change from the current backend.
  • Copies the state from the current backend to the new backend.
  • Updates the local state to point at the new backend.

The course has a dedicated runbook for the migration (Part XXXV).

When local state is appropriate

Local state is appropriate for:

  • Disposable environments. A test environment that gets rebuilt every day.
  • Single-engineer projects. A personal project that one engineer maintains.
  • Module development. A module being developed in isolation.

Local state is not appropriate for:

  • Production infrastructure. A real-world estate that business-critical services depend on.
  • Multi-engineer teams. Two or more engineers working on the same configuration.
  • Compliance-bound environments. Estates that must meet encryption-at-rest, audit, or access-control requirements.

The courses recommendation: default to a remote backend.

What comes next

The next lesson is state security — encryption at rest, access control, audit, and backups.

Verification

Knowledge check · 7 questions

  1. Q1. What is the role of state list?

  2. Q2. What is the role of state mv?

  3. Q3. state rm destroys the real world.

  4. Q4. What is the role of state replace-provider?

  5. Q5. Which state operations mutate the state? (Select all that apply.)

  6. Q6. What is the role of the moved block?

  7. Q7. A team renames a resource in the configuration. The plan proposes to destroy the old and create the new. What is the fix?

Passing score: 75%. Answers are checked in this browser.