skip to content

A Terraform state file carries top-level serial and lineage fields. What is each one for, and what does Terraform do when the lineage of the state it is about to write does not match the one it read?

level: seniorimportance: nice to knowfreq 30%

answer

  1. one counts writes, one names the family
  2. two projects can both sit at serial 42
  3. minted once, carried forward forever
  4. mismatch means wrong file, not old file
  5. push refuses; -force says discard it

basics

~20 s

Serial is a counter Terraform increments on every state write, so it can tell which snapshot is newer. Lineage is a UUID minted when the state was first created, identifying the line of descent; a mismatch means two unrelated states, and Terraform refuses rather than overwriting.

solid answer

~40 s

They answer two different questions about a snapshot. `serial` is a monotonically increasing integer bumped on each write, so Terraform can order snapshots and detect that the stored state has moved on since it was read. `lineage` is a UUID generated once, when a state is first created, and carried forward through every subsequent write of that same state — it identifies the *family*, not the version. Together they let Terraform distinguish "a newer version of my state" from "somebody else's state entirely." A lineage mismatch is treated as the second case: `terraform state push` refuses a state whose lineage differs from the destination's, and refuses one whose serial is older, unless you pass `-force`. In practice a surprise mismatch means someone re-initialised a fresh state or pointed the configuration at the wrong location.

go deeper

for a junior

Recognise the two fields when you see them in a state file and know that Terraform uses them to tell newer snapshots from unrelated ones. You are not expected to have hit a mismatch.

for a middle

Explain each field's job separately — serial as a per-write counter, lineage as a create-once identity — and why a counter alone cannot detect that you are pointed at somebody else's state.

for a senior

Show the operational reflex: a lineage mismatch is a pointing error to diagnose, not a conflict to force through. Be able to say what -force on a state push actually discards and when you would accept that.

for a principal

Own the guardrail design: who may force a state write, how state locations are named and separated so environments cannot be crossed, and why cloning an existing state to seed a new environment defeats the identity check entirely.

## Two fields, two different jobs Open any state snapshot and the header contains both: ```json { "version": 4, "terraform_version": "1.9.5", "serial": 42, "lineage": "3f1c9a5e-7b2d-4a11-9c2f-6d8e4a0b1c23" } ``` `serial` is a version counter **within** one state's history. Terraform increments it every time it writes a new snapshot. Snapshot 42 is strictly later than snapshot 41 of the same state. `lineage` is a UUID generated exactly once — when Terraform first creates a state — and copied unchanged into every later snapshot of that state. It answers "are these two snapshots the same state at different times, or two different states?" Serial alone cannot answer that: two unrelated projects can both be sitting at serial 42, and one is not an update of the other. ## Why the pair is needed Consider three situations a state manager has to tell apart: 1. **Normal write.** Same lineage, serial one higher than what was read. Expected; proceed. 2. **Stale write.** Same lineage, but the stored serial has moved ahead of what this run read — somebody else wrote in between. Terraform must not clobber it, because the newer snapshot contains resources this run never saw. 3. **Wrong state entirely.** Different lineage. This is not a version conflict; it is the wrong file. Overwriting would strand every object the destination state recorded. Terraform treats case 3 as a hard stop rather than a merge, because there is no correct merge. Two states with different lineages describe two independent sets of real objects, and picking one silently abandons the other. ## Where you actually see it enforced The visible enforcement point is `terraform state push`, which is the supported way to write a state document you produced elsewhere (usually via `terraform state pull`, an edit, and a push back). It performs the checks explicitly: it will not push a state whose lineage differs from the destination's, and it will not push one whose serial is behind the destination's. `-force` overrides both, and it exists for the genuine cases — a deliberate restore from an older snapshot, or seeding a state you rebuilt — but it means "I accept that whatever is there is being discarded." ## How mismatches actually arise A lineage mismatch is nearly always a pointing error rather than corruption: - A configuration was re-initialised against an empty location, Terraform minted a **new** lineage, and now someone is trying to reconcile it with the original state. - A state file was copied between projects as a starting point, so two states share the same lineage while describing different objects — the inverse problem, and worse, because the checks pass. - A restore workflow pulled a snapshot out of the wrong environment's storage. When you see the mismatch, the right instinct is to stop and identify which state describes the objects that are actually running, not to reach for `-force`. Ask which snapshot's IDs resolve against the live account. ## Serial in day-to-day life Serial is also the thing you use to reason about backups. When storage keeps historical copies of the state object, each copy is a serial; "restore the previous state" concretely means "restore serial N-1." A local backend writes the previous snapshot alongside the current one for exactly this reason, which gives you one step of history and no more. One caution: serial counts **writes**, not applies. Operations that rewrite state without changing infrastructure — an address move, a persisted refresh — bump it too, so a jump of several serials since yesterday does not by itself mean several deployments happened. ## What a good answer sounds like Name the two jobs — counter versus identity — then give the operational meaning: serial protects you from overwriting a newer snapshot, lineage protects you from overwriting an unrelated one, and `-force` is the escape hatch you use only when you can say out loud what is being discarded. Candidates who have only read about state describe both as "version numbers," which misses the entire point of lineage.

  • When is passing -force to terraform state push actually legitimate?
    When you intend to discard what is at the destination and can say so precisely: restoring a deliberately older snapshot after a bad apply, or seeding a state you rebuilt by re-adopting objects. It is never the right response to a surprise lineage mismatch — that means you have not yet worked out which state describes the live objects.
  • Does the serial number tell you how many times the infrastructure was changed?
    No. It counts state writes, and Terraform writes state for reasons other than changing infrastructure — persisting a refresh, recording an address move, updating outputs. A gap of five serials since yesterday might be one apply and four bookkeeping writes.
  • Two teams copied one state file as a template for a new environment. What problem does that create?
    Both states now share a lineage while describing entirely different objects, so Terraform's identity check silently passes on a push that should have been rejected. The safeguard only catches unrelated states that look unrelated; a cloned lineage defeats it, which is why new environments start from a genuinely new state.

saying these in an interview costs you the question

  • Serial and lineage are both just version numbers
  • A lineage mismatch means the state file is corrupt
  • Terraform merges two states when their lineages differ
  • Serial counts the number of applies performed
  • -force is the normal fix for a rejected push

context