Terraform State Locking: DynamoDB and Consul

Multiple engineers working on infrastructure in parallel often leads to state conflicts and downtime. We design and implement managed state locking turnkey: we set up schemes, policies, and controls that remain with your team. Conflicting applies disappear, and change delivery accelerates significantly.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1365
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

Why Terraform state locking breaks under parallel work

Terraform keeps a map of managed resources in a state file and builds a change plan from its current version. While one engineer applies, everything matches. With two parallel applies, both do not match — and the sign of breakage surfaces not in logs, but in the cloud.

The terraform state locking mechanism covers exactly this case: before apply, the process acquires a lock, the second waits or stops with a message that the lock is already taken.

In object storage, the lock is a DynamoDB table with a lock identifier, in Consul — a session with a limited lifetime. The guarantee is one: exactly one process works with the infrastructure description.

The conflict of simultaneous apply begins where locking is not configured or not held: there is a storage address, but no lock table; the engineer lacks write permissions to it; the local machine and build agent have different credentials; the session lifetime expired during a long apply.

Separately — manually removing the lock after a process crash, without checking that the state write has completed.

Then both processes read one state version and build a plan from it; the first writes the result, the second writes over it its own, assembled from stale data.

Resources of the first drop out of the description: in the cloud they live and are billed, while Terraform believes they do not exist.

This is a corrupted state file — not corrupted in bytes, but incomplete in meaning. It also causes desynchronization: the next run tries to create occupied names or recreates working nodes, and there is nothing to roll back to if versioning is not enabled in the storage. Let's break down how this is covered.

What managed state locking gives the team

State locking is a distributed lease for writing. Before applying a plan, Terraform places an owner marker in shared storage — a row in a DynamoDB table or a lock key in Consul — and removes it only after the operation completes.

The second engineer sees who holds which operation instead of silently overwriting someone else's result.

Without a lease, two simultaneous applies converge into one state file: some resources remain outside the description, some are described twice. The desync is sorted out manually using provider logs — this takes a night instead of ten minutes.

How infrastructure change preparation time is reduced

The engineer works with an accurate picture, not with state that someone edited in parallel, so the plan does not have to be rebuilt and checked manually.

Waiting in the queue is visible in the command output and measured in seconds; for a team of several people, the change goes through on the first apply, without rollbacks for re-preparation.

Which class of failures leaves daily work

The most time-consuming recovery scenario goes away — a lost or partially overwritten state file, which is restored from logs and by manually importing resources.

Automatic lease expiration covers the second half: if an engineer loses access, the marker is removed by timeout and does not remain an eternal stop for the whole team.

State locking options: comparison of operating modes

State locking is a distributed write lock on the state file: before planning or applying, the process acquires it, and other runs wait or fail with a clear error.

In Terraform, the mechanism is enabled at the backend level, so choosing an option is choosing a lock storage, not an add-on over it.

Option When suitable Limitations
DynamoDB with remote backend Environment in one cloud, state in object storage Cloud lock-in; a table per workspace; a stuck lock is removed manually
Consul: key-value and sessions The environment already has a maintained Consul cluster Need to maintain quorum and backups; if quorum is lost, the lock is not granted
Local state file One engineer on their machine Does not protect within a team: parallel runs overwrite state

Locking via managed DynamoDB service

A table with a key by state name, conditional write, and a lifetime field — the lock is set atomically, and the service does not need to be deployed and maintained yourself.

Permissions are granted by an access policy, the table contents can be read with your eyes, which simplifies troubleshooting stuck runs. Limitation of the mechanism: if the process is interrupted, the lock marker remains and is removed by a separate command manually.

Locking via Consul

The session is tied to the agent: if the agent goes down, the key is released automatically, no manual intervention required. This is convenient if Consul is already maintained in the environment: no second cloud dependency and no second set of access permissions appears. The price is cluster maintenance: quorum, backups, updates.

A common limitation of all options: locking protects against simultaneous writing, but not against corruption of the state itself — storage versioning and regular backups remain mandatory.

Stages of implementing state locking in a team

State locking is not a "set and forget" option: it changes how the team works, so we implement it consistently. Below is a setup route for the case where more than one engineer or a build pipeline uses the state.

Audit of current state and simultaneous apply points

  1. We build a map of backends. We record where the state file is located, who accesses it and from which environments, and whether there are runs outside the pipeline.
  2. We look for simultaneous apply points. From logs and team interviews, we note where parallel runs have already caused a conflict or corrupted state.
  3. We define requirements. Number of teams, access separation, lock lifetime, behavior on connection loss — the choice of storage depends on this.

Setup and verification in a training environment

  • We deploy lock storage. A DynamoDB table or Consul cluster with access policies and lock lease duration.
  • We migrate the backend. We move the state and verify that reads and writes go through the new storage, while rollback remains possible.
  • We test locking under load. Two parallel applies: one acquires the lock, the other correctly stops with a clear message.
  • We hand over the solution to the team. Conflict regulations, instructions for removing a stuck lock, engineer training, and an on-call owner.

In a production environment, this closes the class of failures where two applies rewrite state and infrastructure diverges from configuration.

Configurations, diagrams, and regulations that remain with you

After implementation, you are left not only with working infrastructure, but also with a set of artifacts by which it can be developed without our involvement.

Everything is in version control, changes are reviewed before apply, and decisions are recorded next to the code — a new engineer joins the project without oral retellings.

Interaction diagram and access rights description

  • Interaction diagram: which services depend on each other, where network boundaries run, what happens to the system if an individual node fails.
  • Access policies: roles and permissions on a least-privilege basis, separately for people and separately for automation.
  • State storage description: where the Terraform state file is located, which locking mechanism protects against simultaneous changes, and how it behaves on connection loss.
  • External dependency map: image registries, secret stores, entry points — with responsible owners indicated.

State handling regulations and team instructions

  • State handling regulations: who and how removes a lock, what to do in a conflict, and how to restore state from a backup.
  • Deployment and rollback instructions: step-by-step, verified in a test environment.
  • Configuration structure conventions: module breakdown, resource naming, secret storage rules.
  • Checklists for typical operations: onboarding a new environment, decommissioning a node, rotating keys and access credentials.

Case study: how we eliminated conflicting state applies

Project — infrastructure across several environments, six engineers, and about forty changes per week. State was in a single backend without locking: two simultaneous applies — from a branch and from a local machine — overwrote the same file.

Result: state diverging from reality, "orphaned" resources, and manual triage instead of work.

We moved state to a remote backend with locking: Terraform takes a lock in a DynamoDB table before planning and releases it after apply; on the second site, Consul acts as the coordinator.

More important than locking itself was the triage rule: when the lock is held, the engineer sees its owner and removes it only after verifying that the process is indeed not running.

What changed in the team's daily work

Before: on average two state divergences per week, triage from forty minutes to several hours; once recovery from backup took half a workday.

After: over nine months — not a single conflicting apply, and apply time decreased from twenty-one to nine minutes due to eliminated repeated runs and waiting.

A whole class of failures disappeared: state overwrites and resources that exist in the cloud but are missing from the file. Changes became smaller and more frequent — this stopped being a risk.

How does state locking differ in DynamoDB and Consul?

Both mechanisms solve one task: prevent two runs from simultaneously overwriting state and leaving infrastructure half-applied.

The difference is where the lock lives, who removes it on failure, and what must be maintained so that locking itself does not become a source of incidents.

DynamoDB is locking by write: before a change, Terraform makes a conditional write to a table with the run identifier, and a parallel attempt is rejected.

The table and permissions to it must be created and maintained, but a separate cluster is not needed — everything lives where state does. Failure behavior is rigid: if the process is interrupted or the agent dies, the record remains, and it can only be removed forcibly. Until then, all builds are stopped.

Consul is session-based locking: the agent opens a session with a limited term and constantly renews it; the lock is tied to the session and is released automatically when renewal stops.

Manual intervention when the agent dies is not required, but this is paid for with maintenance: a fault-tolerant cluster, access tokens, and agent monitoring are needed.

There is also the opposite risk — during a network failure, renewal does not go through, and the lock is removed while apply is still running if the session term is set shorter than the longest apply.

The practical selection criterion is already deployed infrastructure. Where state and workloads live in the cloud, DynamoDB adds one table and nothing more; deploying Consul only for locking is an extra cluster to maintain. If Consul already handles service discovery, locking comes without new infrastructure.

In both cases, the session term is selected for the maximum apply, and the procedure for removing stuck locks is documented in advance — so you do not have to figure it out when the pipeline is stopped.

Technical doubts about state locking

State locking protects against the situation where two applies simultaneously write to one file. At the start it raises almost no questions — they appear in operation: removing a stuck lock, changing storage, agreeing on rules for those who work locally.

The apply failed, but the lock remains. What to do?

First we make sure the process is truly dead: we look at the owner identifier and capture time. If this is a connection loss with an external service rather than a live run, we remove the lock forcibly — but only after verifying that no one is executing a plan.

Then we add a capture timeout limit and retries so that a network interruption does not require manual intervention.

How to migrate from one lock storage to another without downtime?

We perform the migration in two steps: first we move the state file and verify that the plan matches the real infrastructure, then we change the locking settings and close the old environment for writes.

We run the new mechanism in advance in a separate environment — including the simultaneous run scenario.

Part of the team applies configuration locally — is that acceptable?

Local state copies are a dead end: changes on one engineer's machine are not visible to others, and divergences surface at the next shared apply. One option is acceptable — local runs work with shared state and locking. We move experiments to separate environments with isolated state.

How to handle an incident if state has already been corrupted?

We record the point: the state file version from storage and the change history. Then we reconcile resources with a plan, return missing objects to state, and remove extra records. After that — protection against forced lock removal and a ban on applies from local copies.

Let's discuss the task: we'll select a locking scheme for your team

State locking cannot be configured from a template: the same scheme with two engineers and with twenty gives a different result.

Therefore, before estimation and a work plan, we examine your environment and operating conditions rather than offer a ready-made solution from a catalog.

  • How many engineers work with state simultaneously and whether there are automated runs — this determines how sensitive locking should be to conflicts.
  • How often infrastructure changes: rare edits and a continuous flow of changes require different lock retention times and different reactions to stuck cases.
  • Which mechanism is used now — a local file, locking in cloud storage, or Consul — and which failures have already led to state divergence.
  • Rights and access: who has the right to remove a stuck lock, how this is recorded in logs, and who receives a notification about removal.
  • Whether isolation by environment is needed — test, pre-production, and production are locked separately or by one mechanism with access separation.
  • The handover procedure for the solution: configurations, operating diagram, lock removal regulations, and instructions for the on-call engineer.

Describe the task in general terms — we will return with an estimate and work plan and select a locking scheme for your team.