TerraformTerraform

Error: Backend configuration changed

Terraform detected the backend block differs from the one recorded in .terraform. How to migrate state deliberately instead of reinitialising and losing track of it.

medium fix5 min read

the terraform error
Error: Backend configuration changed
A change in the backend configuration has been detected, which may require migrating existing state.

Error: Backend initialization required, please run "terraform init"

Error: Initialization required. Please see the error message above.

Do this first3 steps

Run these in order. Each one tells you what its output means before you change anything.

  1. 1

    Compare what is recorded against what the configuration now says

    python3 -c "import json;d=json.load(open('.terraform/terraform.tfstate'));print(json.dumps(d.get('backend',{}).get('config',{}),indent=2))" 2>/dev/null

    .terraform/terraform.tfstate records the backend Terraform last initialised with. Diffing it against your backend block shows exactly which key changed, which decides whether you need migrate-state or reconfigure.

  2. 2

    If the state should move to the new location, migrate it

    terraform init -migrate-state

    This copies the existing state into the new backend and prompts for confirmation. Use it when you are genuinely relocating state, such as moving from local to S3 or changing the key.

  3. 3

    If the state is already where it should be, just reinitialise

    terraform init -reconfigure

    This discards the recorded backend and initialises afresh without copying anything. Use it when only credentials or a role changed, or when the new backend already holds the correct state.

All 8 sections

Terraform records which backend it last initialised with in .terraform/terraform.tfstate. When the backend block differs, it stops and asks, because the two possible intentions have very different consequences.

Migrating copies the existing state to the new location. Reconfiguring forgets the old backend and starts using the new one as-is.

Pick the wrong one and you either duplicate state or lose track of it.

See what changed

python3 -c "import json;d=json.load(open('.terraform/terraform.tfstate'));print(json.dumps(d.get('backend',{}).get('config',{}),indent=2))"
{
  "bucket": "acme-tfstate",
  "key": "prod/terraform.tfstate",
  "region": "eu-west-1"
}

Compare against your backend block. Any difference triggers this, including ones that do not change where state lives:

ChangedUsually want
bucket or key-migrate-state
Local to remote, or the reverse-migrate-state
role_arn, profile, credentials-reconfigure
use_lockfile, dynamodb_table-reconfigure
region, with the bucket moved-migrate-state
region, correcting a typo-reconfigure

Migrating

terraform init -migrate-state
Do you want to copy existing state to the new backend?
  Pre-existing state was found while migrating the previous "local" backend
  to the newly configured "s3" backend. No existing state was found in the
  newly configured "s3" backend. Do you want to copy this state?

  Enter "yes" to copy and "no" to start with an empty state.

Read that prompt properly. Answering no gives you an empty state while your infrastructure still exists, and the next apply tries to create everything again.

Back up first, always:

terraform state pull > backup-$(date +%Y%m%d-%H%M).tfstate

Afterwards, verify:

terraform state list | wc -l
terraform plan          # should show no changes

A plan proposing to create everything means the migration did not carry the state. Restore the backup and try again:

terraform state push backup-20260921-1430.tfstate

Reconfiguring

terraform init -reconfigure

No copying, no prompt. It discards the recorded backend and initialises the new one.

Correct when:

  • Only credentials, a profile or a role changed
  • The target backend already holds the right state, such as a colleague's machine initialising against a shared backend for the first time
  • You are switching workspaces or environments that have their own state

Wrong when the state has not been copied yet, because you end up pointing at an empty backend with real infrastructure behind it.

The safe default in CI

terraform init -reconfigure -lockfile=readonly

CI runners start clean, so there is no .terraform directory and nothing to migrate. -reconfigure makes the behaviour deterministic and stops a cached workspace from producing a surprise prompt. -lockfile=readonly fails the build if the provider lock file would need to change.

Never use -migrate-state in automation. It is interactive by nature, and the one time it does something unexpected you want a human present.

Partial configuration

Hardcoding a bucket name means a different file per environment. Leave the backend block empty and supply values at init:

terraform {
  backend "s3" {}
}
terraform init -reconfigure \
  -backend-config="bucket=acme-tfstate" \
  -backend-config="key=prod/terraform.tfstate" \
  -backend-config="region=eu-west-1"

Or with a file per environment:

# backends/prod.hcl
bucket = "acme-tfstate"
key    = "prod/terraform.tfstate"
region = "eu-west-1"
terraform init -reconfigure -backend-config=backends/prod.hcl

Note that backend blocks cannot use variables or expressions. They are evaluated before anything else, so key = "${var.env}/terraform.tfstate" is rejected. Partial configuration exists precisely because of that limitation.

Switching environments in one directory

terraform init -reconfigure -backend-config=backends/staging.hcl
terraform plan

The -reconfigure is essential here. Without it Terraform offers to migrate production state into the staging backend, and a hurried yes merges two environments.

Workspaces avoid the whole issue for environments that share configuration:

terraform workspace select prod

They share one backend with separate state paths, so there is no re-init between environments.

A checklist

  1. Read the recorded backend from .terraform/terraform.tfstate.
  2. Diff it against your backend block to see which key changed.
  3. State needs to move → -migrate-state, after terraform state pull > backup.
  4. Read the migration prompt. Answering "no" gives you an empty state.
  5. Verify with terraform state list and a plan showing no changes.
  6. Only credentials or locking changed → -reconfigure.
  7. In CI, always -reconfigure, never -migrate-state.
  8. Use partial configuration with -backend-config for per-environment values.

Frequently Asked Questions

What is the difference between -migrate-state and -reconfigure?

-migrate-state copies the existing state from the old backend into the new one and prompts you to confirm. -reconfigure discards Terraform's record of the previous backend and initialises the new one without copying anything. Use migrate when state genuinely needs to move, such as going from local to S3 or changing the key. Use reconfigure when the state is already in the right place and only something like credentials or a role changed.

Why does Terraform ask about this when I only changed the IAM role?

Because it compares the entire backend configuration against what it recorded at the last init, and any difference triggers the check. It has no way to know that role_arn affects how it authenticates rather than where state lives. This is the clearest case for -reconfigure: the state has not moved, so there is nothing to migrate and copying would be wrong.

What happens if I answer "no" to the migration prompt?

Terraform initialises the new backend with an empty state while your real infrastructure carries on existing. The next plan then proposes creating everything from scratch, and applying it either fails on resources that already exist or creates duplicates. Always run terraform state pull > backup.tfstate before a migration, and after it verify with terraform state list and a plan that shows no changes.

Why can I not use variables in a backend block?

Because the backend is initialised before Terraform evaluates variables, locals or anything else in the configuration, so there is nothing available to interpolate. That is why partial configuration exists: leave the block empty and supply the values at init time with -backend-config flags or a .hcl file per environment. It keeps a single set of Terraform files working across environments without hardcoding a bucket and key.

Should I use -reconfigure in CI?

Yes, always, and never -migrate-state. CI runners start from a clean checkout with no .terraform directory, so there is no previous backend to migrate from and nothing to copy. -reconfigure makes the behaviour deterministic even if a cached workspace is reused. -migrate-state is interactive by design and belongs in a session where a person can read the prompt and decide, which is exactly what a pipeline cannot do.

Reference and practice

Learn the underlying concept

Other Terraform errors