Skip to content

Remote State and Backends


A local state file works surprisingly well right up until a second person needs to use it. At that point, the question is no longer “where did Terraform save my state?” but “where is the team’s authoritative state, and how do we safely coordinate access to it?”

This chapter introduces Terraform backends and explains why moving state remotely is an architectural decision involving collaboration, security, locking, availability, and recovery.

What Is a Terraform Backend?

A Terraform backend determines how Terraform stores and accesses state.

With the default local backend, state is stored on the local filesystem. With a remote backend, Terraform communicates with a shared system instead.

The basic relationship is:

Terraform Configuration
          │
          ▼
     Terraform CLI
          │
          ▼
       Backend
          │
          ▼
    Terraform State

The backend therefore sits at an important boundary between Terraform’s execution and its persistent state.

Terraform’s backend documentation describes backends as responsible for state storage and, when supported, state locking.

Note

A backend is more than “the place where a .tfstate file lives.” It defines how Terraform accesses persistent state and, depending on the backend, how concurrent operations are coordinated.

Why Remote State Exists

A local backend is convenient for learning and for some isolated projects.

For a team, it creates several problems.

Suppose Alice and Bob both work on the same infrastructure:

Alice's laptop                    Bob's laptop
      │                                │
      ▼                                ▼
 local state                       local state
      │                                │
      └───────────┬────────────────────┘
                  ▼
          Shared infrastructure

Each local state file is a separate copy.

The team now has to somehow ensure that every copy remains consistent.

That is exactly the problem remote state is designed to solve.

With shared state:

Alice ──┐
        │
Bob ────┼──► Shared Backend ──► State
        │
CI ─────┘

Everyone uses the same authoritative state.

When combined with state locking, the backend can also coordinate state-changing operations.

Local and Remote Backends

The local backend is the simplest:

Terraform
   │
   ▼
Local filesystem
   │
   ▼
terraform.tfstate

It is useful when:

  • You are learning Terraform
  • You are experimenting locally
  • The infrastructure is intentionally personal
  • There is no shared workflow

A remote backend is more appropriate when:

  • Multiple engineers manage the same infrastructure
  • CI/CD runs Terraform
  • State must survive laptop loss
  • State access needs centralized control
  • State locking is required
  • State recovery needs stronger operational controls

Remote backends do not automatically make infrastructure safe. They give you a foundation on which safe team operations can be built.

State Storage and State Locking

Keep these concepts separate:

                Backend
                   │
          ┌────────┴────────┐
          │                 │
          ▼                 ▼
    State Storage       State Locking
          │                 │
       "Where?"          "Who writes?"

A backend can store state without providing appropriate locking.

That situation is dangerous for shared infrastructure because two Terraform processes could potentially operate against the same state without coordination.

When a backend supports locking, Terraform locks the state during operations that could write it.

Backend Configuration

Backend configuration is declared inside the Terraform configuration.

For example:

terraform {
  backend "s3" {
    bucket       = "example-terraform-state"
    key          = "network/terraform.tfstate"
    region       = "us-east-1"
    use_lockfile = true
  }
}

The exact settings depend on the backend.

Notice that the backend is configured in a terraform block rather than as a normal resource.

That distinction matters.

A backend is part of Terraform’s own initialization and state-management mechanism. Terraform must know how to access the backend before it can fully operate with the state stored there.

Backend Initialization

When you add or change a backend configuration, run:

terraform init

Terraform initializes the working directory and configures the backend.

If Terraform discovers that existing state needs to move from one backend to another, it can ask whether you want to migrate it.

A typical transition is:

Local State
    │
    │ terraform init
    │
    ▼
Remote Backend
    │
    ▼
Remote State

Do not blindly accept backend migration prompts in production.

First verify:

  • The bucket or remote service is the intended destination
  • The state key is correct
  • Credentials point to the intended account
  • The configuration is using the expected workspace
  • The destination is accessible
  • The existing state is backed up when appropriate

Migrating Existing State

Suppose you start with:

project/
├── main.tf
├── variables.tf
└── terraform.tfstate

You later add a remote backend.

Terraform detects that the state already exists locally and that the backend configuration has changed.

Run:

terraform init

Terraform can offer to migrate the existing state.

After migration, verify the backend and state before doing a real infrastructure change:

terraform plan

A successful migration should normally result in Terraform understanding the same infrastructure without proposing to recreate everything.

Tip

A surprisingly large and destructive plan immediately after backend migration is a reason to stop and investigate, not a reason to approve the plan.

Reconfiguring a Backend

Sometimes you intentionally want Terraform to initialize again using different backend settings.

For example:

terraform init -reconfigure

This tells Terraform to disregard its previous backend initialization information and configure the backend again.

This is useful when the backend configuration has changed and you deliberately want to reinitialize rather than migrate existing state.

The important distinction is:

terraform init
    │
    └── may detect and offer state migration

terraform init -reconfigure
    │
    └── reinitialize backend configuration

Do not use -reconfigure as a casual troubleshooting command without understanding what backend you are connecting to.

Backend Misconfiguration Can Be Dangerous

Imagine production should use:

bucket = company-prod-state
key    = payments/terraform.tfstate

but someone accidentally configures:

bucket = company-dev-state
key    = payments/terraform.tfstate

Terraform may now operate against a completely different state.

The configuration can still be syntactically valid.

The credentials can still be valid.

terraform init can still succeed.

That is why backend configuration deserves the same care as infrastructure configuration.

Useful checks include:

  • Account or subscription identity
  • Region
  • Bucket/container/project name
  • State key/path
  • Workspace
  • IAM permissions
  • Locking configuration

Backend Credentials

Avoid hardcoding cloud credentials in backend configuration.

For example, do not turn this into a source-controlled secret:

terraform {
  backend "s3" {
    access_key = "..."
    secret_key = "..."
  }
}

HashiCorp recommends using environment variables and shared credential mechanisms rather than putting sensitive backend credentials directly into configuration. Backend documentation also warns that some backend configuration mechanisms can cause sensitive values to be persisted in .terraform data or plan files.

The same principle applies to CI.

Backend authentication should come from a controlled credential mechanism, not from values committed into Git.

Backend Bootstrapping: The Chicken-and-Egg Problem

There is an interesting problem when Terraform itself is supposed to create its own backend.

Suppose you want Terraform to create:

S3 bucket
    │
    └── stores Terraform state

But Terraform needs the S3 bucket before it can use the S3 backend.

You cannot depend on the backend state to manage the infrastructure that creates the backend in the first place without creating a circular dependency.

Conceptually:

Terraform needs backend
        │
        ▼
S3 bucket must exist
        │
        ▼
Terraform should create S3 bucket
        │
        ▼
Terraform needs backend

A Common Bootstrap Strategy

Separate the bootstrap infrastructure from the workloads it will eventually manage.

For example:

bootstrap/
├── main.tf
└── outputs.tf

infrastructure/
├── backend.tf
├── main.tf
└── variables.tf

The bootstrap configuration initially uses local state.

It creates the remote backend infrastructure.

Then the main infrastructure configuration is initialized against that backend.

Bootstrap Terraform
   │
   │ local state
   ▼
Creates S3 backend
   │
   ▼
Main Terraform
   │
   │ remote backend
   ▼
Managed infrastructure

This is not a flaw in Terraform. It is a boundary in the system: something has to exist before Terraform can use it as its state service.

Warning

The bootstrap state is itself important. Do not create a production state bucket and then forget about the state that created the bucket. Give bootstrap infrastructure an explicit ownership and recovery strategy.

Backend Design Affects More Than Collaboration

Choosing a backend affects:

Collaboration

Can multiple engineers and CI access the same authoritative state?

Security

Who can read or modify state?

Availability

What happens if the backend is temporarily unavailable?

Locking

Can concurrent writers be coordinated?

Disaster Recovery

Can a deleted or corrupted state snapshot be recovered?

Access Control

Can production state be separated from development state?

These are infrastructure architecture questions, not merely Terraform syntax questions.

Remote State Does Not Mean “No Local Files Ever”

With a non-local backend, Terraform normally does not persist the remote state as a normal state file on the local filesystem. In some unrecoverable backend-write failures, Terraform may write state locally to prevent data loss. Terraform can also explicitly retrieve remote state:

terraform state pull

And it can manually push state:

terraform state push state.json

Warning

The second operation (push) is dangerous because it can overwrite remote state.

Use it only for deliberate recovery work.

A Team Mental Model

At this point, think of Terraform as:

                    Git
                     │
              Configuration
                     │
                     ▼
Engineer / CI ──► Terraform
                     │
                     ▼
                  Backend
                ┌────┴────┐
                ▼         ▼
             State      Lock
                │
                ▼
        Cloud Infrastructure

Git answers:

What configuration should Terraform execute?

The backend answers:

What state should Terraform use, and how should shared access be coordinated?

The cloud provider answers:

What infrastructure actually exists?

Those systems work together, but they are not interchangeable.

Where We Go Next

A remote backend becomes much easier to understand when you build one.

In the next chapter, you’ll implement an AWS-based backend using S3, including encryption, versioning, access control, and the current S3-native state-locking mechanism.

That turns the abstract backend architecture into something you can operate.

Last updated on