Choosing a Terraform State Backend and Locking Strategy for Shared Infrastructure
Compare local state, S3 with DynamoDB, S3 native lockfile, and hosted remote backends for multi-operator Terraform workflows, then implement and validate a shared backend.
06 Sept 2025, 12:18 UTC

The shared-state problem
When more than one engineer or CI pipeline runs terraform apply against the same infrastructure, local state breaks down: it lives on a single disk, offers no locking, and gives teammates no visibility into what has been deployed. The practical decision is not whether to use remote state, but which backend balances consistency guarantees, IAM complexity, and operational burden for your team.
Decision constraints
- Operator count: Two or more humans or automated runners make local state unsafe.
- Cloud footprint: AWS-only teams can use S3 backends; multi-cloud or air-gapped teams may prefer self-hosted or commercial options.
- Auxiliary resource tolerance: Are you willing to manage a DynamoDB table alongside the S3 bucket?
- Version floor: Does your Terraform release support the backend features you plan to use?
- Compliance: Do you need audit trails, RBAC, or run history that a raw object store does not provide?
Backend options compared
| Option | Locking mechanism | Shared visibility | Additional AWS resources | Version sensitivity |
|---|---|---|---|---|
| Local | None | No | None | No |
| S3 + DynamoDB | DynamoDB conditional writes | Yes | S3 bucket + DynamoDB table | No |
| S3 native lockfile | S3 conditional writes via lockfile object | Yes | S3 bucket only | Yes |
| Hosted remote (e.g., Terraform Cloud) | Server-side | Yes | None (vendor-managed) | No |
Trade-offs that matter
IAM complexity. S3 with DynamoDB requires IAM policies for both the state bucket and the lock table. Native S3 lockfile reduces the policy surface to S3 permissions, but concentrates availability risk in a single service.
Lock latency and consistency. DynamoDB conditional writes are a long-established pattern with predictable behavior. Native S3 lockfile depends on S3 consistency guarantees and newer backend code paths; test contention under load before relying on it in production.
State-loss blast radius. All remote options move state off individual workstations. Hosted backends add vendor dependency, network-path requirements, and subscription pricing, but shift backup, encryption, and access control to the provider.
Implementation: S3 backend with native lockfile
The following configuration uses S3 native lockfile to avoid managing a DynamoDB table. Backend blocks cannot use variables or interpolation, so environment-specific values are supplied at initialization time.
terraform {
backend "s3" {
bucket = "myorg-terraform-state"
key = "prod/network/terraform.tfstate"
region = "us-east-1"
encrypt = true
use_lockfile = true
}
}
Create a partial configuration file for each environment and pass it during init. Run these commands from the root of the Terraform working directory; the IAM principal must have s3:GetObject, s3:PutObject, and s3:DeleteObject on the state bucket, plus permission to write the lockfile object.
# backend-prod.hcl
bucket = "myorg-terraform-state"
key = "prod/network/terraform.tfstate"
region = "us-east-1"
encrypt = true
use_lockfile = true
terraform init -backend-config=backend-prod.hcl
If you need DynamoDB locking instead, replace use_lockfile with dynamodb_table. The table must have a partition key named LockID; omitting it causes obscure acquisition failures.
Validation steps
- Offline syntax check. Run
terraform init -backend=falseto validate the backend block without contacting AWS. - Connectivity check. Run
terraform initto confirm the bucket is reachable and the lockfile can be created. - Version check. Run
terraform versionandterraform providersto confirm your release supportsuse_lockfilebefore removing DynamoDB. - Read-path exercise. Run
terraform planin CI to ensure the read lock and state download work from a clean runner. - Lock contention test. Launch two
terraform applyruns simultaneously against the same key. The second should exit with a state lock error. If both proceed, locking is not working. - Encryption check. Inspect the S3 object metadata for server-side encryption headers and confirm bucket versioning is enabled.
Switching backends and rollback
Changing the backend block is a state-changing operation. Terraform will prompt to migrate existing state to the new location during terraform init. Coordinate with all operators to ensure no applies are in flight, otherwise state can diverge. Rollback means reverting the backend block and re-running terraform init to copy state back to the previous location; this must also be done exclusively.
Limitations
- State files contain plaintext secrets even when encrypted at rest; restrict bucket policies and IAM roles tightly.
- Native S3 lockfile support is version-sensitive; confirm your Terraform version supports
use_lockfilebefore removing DynamoDB. - Backend blocks cannot use variables or interpolation; use partial configuration or
-backend-configfiles. - Hosted backends introduce vendor dependency, network-path requirements, and subscription pricing.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.