Managing Terraform State: Local, Locking, and Caution
Manage Terraform state safely: keep it in a remote backend with locking, never hand-edit it, and use state commands for real renames.
Netbay Developer Relations
Netbay Engineering
On this page
State is the single most underrated file in a Terraform project. It maps every resource in your configuration to the real object in the cloud, holds IDs and attributes Terraform reuses across runs, and routinely contains sensitive values. Teams that treat it casually pay later in confusing plans, double-deployed resources, and the classic surprise of a deleted state file hiding infrastructure that keeps billing. This post covers making state safe.
The Problems with Local State
By default Terraform writes terraform.tfstate next to your code. For a solo project that is workable but already risky:
- The file sits in your working tree, ripe for accidental commits to git.
- Two people running plan and apply from different machines will build two different realities that fight each other.
- Disaster recovery is a literal single file; lose it and Terraform forgets what it manages.
The file may contain values marked sensitive, so committing it leaks secrets in the same commit that leaks infrastructure layout. The moment a second pair of hands touches the project, local state stops being acceptable.
Remote Backends and Locking
A backend moves state off your laptop. Using object storage plus a locking table gives you a single source of truth that multiple operators share, with a lock guaranteeing that only one apply runs at a time:
terraform {
backend "s3" {
bucket = "iac-state"
key = "netbay/prod.tfstate"
region = "ap-south-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}After editing the backend block, run terraform init -reconfigure; Terraform moves the existing state into the remote location and from then on every plan reads from there. The lock is what prevents two engineers from applying conflicting changes simultaneously — the error "Error acquiring the state lock" is a feature, not a bug.
How to Update State Without Shooting Yourself
Never hand-edit the state file, and do not blindly rename a resource in code — Terraform would destroy the old one and create a new one. The state subcommands exist for real situations:
terraform state list
terraform state show hcloud_server.web
terraform state mv hcloud_server.web hcloud_server.web_new
terraform state rm hcloud_server.deprecated
terraform import hcloud_server.existing 123456state list inventories what Terraform tracks; state mv tells Terraform a resource moved address without touching the cloud object; state rm forgets a resource so it will no longer be managed (the real object stays alive); and import adopts something created out-of-band into management. Each of these is a surgical edit that keeps the cloud state and the state file in agreement.
Takeaway
Move state to a remote backend with locking before your project is shared, keep hand-edits out entirely, and reach for the state subcommands when real-world renames and adoptions show up. Good state hygiene is what makes git-based reviewability of infrastructure actually trustworthy. When you are ready to test your Terraform against a disposable machine, deploy and destroy a test server on Netbay freely — netbayhosts.in provisions in under 60 seconds and bills only what you keep running.
Keep reading
Follow along on a real VPS
Deploy Linux in under 60 seconds
These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.
Deploy an instance