Infrastructure State Partition
also called State Splitting, Per-Domain State
Splitting one infrastructure state file into several along ownership and lifecycle boundaries, so plans are fast and a mistaken apply cannot reach unrelated resources.
One state file holds 4,000 resources: networking, databases, Kubernetes, DNS. A plan takes 14 minutes, four teams contend for the lock, and every apply is an opportunity to change anything. That last property is the real problem: the state file is the blast radius, and a single state means a typo in a Kubernetes module and the production VPC are in the same transaction.
Partitioning splits it into several states with explicit interfaces between them. The boundary that works is ownership and lifecycle, not technology: the network and DNS zones change rarely and are shared; workloads change daily and are per team; databases sit between. A state per tool is a filing decision that leaves the coupling intact.
Why it matters
Blast radius, lock contention and plan duration all scale with the size of a single state, and all three are felt as slowness until the day one of them is felt as an outage. A 14-minute plan also has a second-order effect: nobody reads it. A plan long enough to skim is a plan whose 23 unexplained changes get applied along with the one-line fix.
Partitioning also makes ownership real. A state per team is a boundary that can be granted credentials, reviewed by its owners and audited independently, which is the thing that makes least privilege possible in infrastructure at all.
Implementation patterns
- Freeze structural change, not all change, during the migration. Attribute changes are absorbable; a moving resource set is not.
- Define the interfaces before moving anything. Each state publishes outputs — VPC id, subnet ids, cluster endpoint — and consumes others. Prefer looking values up by tag or name over a remote state reference, because a reference creates a hard dependency that bites during an incident.
- Move one leaf domain first, and the least critical one. DNS or observability, never the network.
- Use reviewable moves.
movedblocks and import blocks appear in a plan; hand-run state commands do not. Back up every state file before each step. - Insist on an empty plan on both sides after each move. A proposed create means the import did not match; a proposed destroy means you are one apply from deleting production.
- Assert resource-id uniqueness across all state files in CI. Two states owning one resource produce alternating changes; a resource in neither is silently unmanaged. This check is what makes the migration safe and almost nobody writes it.
- Keep the old state until every domain has migrated and survived a normal change cycle.
Industry example
The pattern is the infrastructure form of the same reasoning that produces cell-based architectures and per-team service ownership: limit what one mistake can reach, and align the technical boundary with the human one. Shopify's pod architecture and the general move to cells are the application-layer version of it, and the infrastructure-as-code community arrived at the same place from the other direction, with per-environment and per-domain state layouts becoming the default recommendation as estates grew past a few hundred resources. The recurring lesson in both is that the unit of failure should be the unit of ownership.
Failure scenarios
- A resource in two states, each applying its own view, producing changes that alternate on every run.
- A resource in neither state, unmanaged and unnoticed until something changes it by hand.
- Drift introduced by the move, where the new configuration differs subtly from the original; caught only if you insist on the empty plan.
- A cross-state remote reference failing during an incident, so the state you need to change cannot plan because another state's output is unavailable.
state rmbefore verifying the import, which is the one irreversible step, recoverable only from a backup and a written mapping.- Boundaries drawn by tool, leaving the same coupling with four times the pipelines.
Trade-offs
| Choose | Gains | Pays |
|---|---|---|
| One large state | Simple; no cross-state wiring | Long plans, lock contention, unlimited blast radius |
| Per-domain states | Fast plans, contained mistakes, real ownership | Interface design, more pipelines and credentials |
| Remote state references | Strong typing of dependencies | A hard runtime dependency between states |
| Lookup by tag or name | Loose coupling; independent applies | Depends on a naming convention someone must enforce |
The migration itself is a real cost: 2 to 3 weeks of elapsed time for 4,000 resources if you have done it before, 6 weeks otherwise, most of it waiting for verification cycles, with the first domain taking as long as the other three.
When not to use it
If the plan is slow and nobody is blocked, do not do this. The justification is lock contention, blast radius and ownership, so a single team making weekly changes is better served by one state, and the split would add four pipelines, four credential sets and a dependency graph for no benefit. If the only complaint is plan duration, targeted plans and provider caching are a day of work against a six-week migration. Split state when independent teams need to apply independently, and not before.
Interview question
Q: One Terraform state holds your whole production estate. Tell me whether to split it, how you would sequence the split without downtime, and how you would know it worked.
What a strong answer covers: justifying the split on blast radius, lock contention and ownership rather than on tidiness; boundaries by lifecycle and ownership rather than by technology; defining inter-state interfaces first and preferring tag lookups to remote state references; migrating leaf domains first with reviewable moves and state backups; the empty-plan-on-both-sides criterion and what a create or destroy in it means; the uniqueness check in CI; naming state rm as the point of no return; and an honest elapsed-time estimate.
Quick check
Quiz: What is the point of no return when moving a resource between state files? Removing it from the old state, which is why the order is import, verify an empty plan, then remove — with a backup and a written mapping as the only rollback.
Flashcard: Which CI check makes a state split safe? — One that lists resource ids across every state file and asserts uniqueness, because a resource owned by two states produces alternating changes and one owned by none is silently unmanaged.