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 StateThe 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 infrastructureEach 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.tfstateIt 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 initTerraform 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 StateDo 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.tfstateYou later add a remote backend.
Terraform detects that the state already exists locally and that the backend configuration has changed.
Run:
terraform initTerraform can offer to migrate the existing state.
After migration, verify the backend and state before doing a real infrastructure change:
terraform planA 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 -reconfigureThis 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 configurationDo 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.tfstatebut someone accidentally configures:
bucket = company-dev-state
key = payments/terraform.tfstateTerraform 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 stateBut 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 backendA 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.tfThe 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 infrastructureThis 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 pullAnd it can manually push state:
terraform state push state.jsonWarning
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 InfrastructureGit 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.