Vylara
infrastructure-as-codeci-cdaws

Infrastructure State Collaboration Without a Team Meeting

Safe infrastructure collaboration needs exactly two things: remote state and state locking. Here's how to wire them on AWS for under $0.10/mo — or skip it.

Written byVylara Team
7 min read
Infra State Collaboration

Safe infrastructure-as-code collaboration comes down to exactly two things: remote state stored in a shared location, and state locking so only one apply runs at a time. Wire up both and any number of engineers can manage the same AWS environment without overwriting each other; skip either one and you’ll eventually create duplicate databases or silently clobber a teammate’s change. Everything else — workspaces, CI gating, code review — is refinement on top of those two primitives. This post explains how to implement them for a small team on AWS, what they actually cost (under $0.10/month for a typical team), and the failure modes that make people swear off shared state in the first place.

Why local state breaks the moment a second person joins #

An infrastructure-as-code tool keeps a state file — the mapping between your config and the real resources it created. By default that file lives on the laptop of whoever ran the last apply. The first time a teammate clones the repo and applies, the tool sees an empty state, assumes nothing exists, and tries to create a second copy of your database, your load balancer, and your network. You now have duplicate infrastructure and a state file that disagrees with reality. The fix is to move that file off laptops and into a shared backend that everyone reads from and writes to.

On AWS the canonical pattern is an S3 bucket for the state file and a DynamoDB table for the lock. S3 holds the authoritative state; DynamoDB holds a single row that says "someone is applying right now, wait your turn." When a second apply starts mid-run, it polls the lock instead of stomping the first one. This is genuinely cheap: an S3 bucket holding a few hundred KB of state costs well under a cent a month, and a DynamoDB lock table on pay-per-request billing runs a few pennies even under heavy use. A small team almost never crosses $0.10/month for the backend itself. The real cost is operational, not financial — and that’s the part this post is honest about.

The minimal backend that actually works #

A working remote backend for a small team needs four things: versioning enabled on the bucket so a corrupted apply doesn’t lose your only state, encryption at rest, blocked public access, and a lock table keyed on a single lock identifier. The backend configuration itself is short:

json
{
  "backend": {
    "s3": {
      "bucket": "acme-iac-state",
      "key": "prod/app.tfstate",
      "region": "us-east-1",
      "dynamodb_table": "acme-iac-locks",
      "encrypt": true
    }
  }
}

The chicken-and-egg problem is real: the bucket and table can’t be created by the same state they’re meant to hold. Bootstrap them once with a tiny separate config (or the AWS CLI) and keep that bootstrap state local — it’s a handful of resources that almost never change. After that, every other workspace points at the shared backend. Use a distinct state key per environment (prod/app.tfstate, staging/app.tfstate) so a botched staging apply can never touch production state. This separation is also what stops the kind of environment confusion we covered in your staging environment is lying to you — drift is much easier to reason about when each environment owns its own state.

Locking is what saves you from the 2pm double-deploy #

Remote state without locking is a trap. Two engineers both run an apply within the same minute, both read the same starting state, both compute plans against it, and the second write silently overwrites the first. The DynamoDB lock prevents that: the second apply blocks until the first releases. If a run dies mid-apply — laptop sleeps, CI runner gets killed — the lock can get stuck, and you’ll need a force-unlock command to clear it. Do that only after you’ve confirmed nobody is actually mid-run, because force-unlocking a live apply reintroduces exactly the race you were trying to prevent.

The more durable move for a team is to stop applying from laptops at all. Run plan and apply from CI, gate apply behind a manual approval, and let the pipeline hold the lock for the duration of the run. Humans review the plan; the runner does the write. We argue this same point about machine-generated infrastructure in AI can write your config, it still shouldn’t apply it unsupervised: the value of a checkpoint is that a person sees the diff before anything mutates production. Locking and human gates solve the same problem from two directions — one serializes writes, the other inserts judgment before a write happens at all.

Multi-tenant and many-environment setups raise the stakes #

Once you have more than a couple of environments — or you’re managing infrastructure on behalf of multiple customers — the blast radius of a shared lock and a single state file grows fast. A single 50MB monolithic state file also makes every apply slower, because the tool refreshes every resource in it before planning. The right answer is isolation: separate state keys, separate lock entries, and ideally disposable runners that hold credentials only for the length of one apply. We go deep on that in multi-tenant infrastructure without nightmares. The headline is that one giant state file is the thing that turns a small mistake into a company-wide incident, and splitting state by boundary is the cheapest insurance you can buy.

The honest alternative: don’t manage state yourself #

All of the above is real work that doesn’t ship features. Bootstrapping backends, tuning lock tables, writing the CI approval gate, and clearing stuck locks at 2am are the recurring tax of running infrastructure-as-code as a team. Vylara exists to take that tax off your plate. You install the Vylara GitHub App and select a repository, the Vylara agent analyzes your code and detects what your app needs — Postgres, Redis, storage, queues, all inferred from your dependencies — and proposes the infrastructure. You review the generated configs as a diff with plain-language explanations, and nothing touches your repo without your approval. The agent engine manages the state and locking for the AWS infrastructure it provisions, so there’s no S3 bucket for you to bootstrap and no DynamoDB lock to force-unlock.

Two honest caveats. First, this is AWS-only today — GCP and Azure are on the roadmap, not shipping. Second, connecting your repo and merging a PR does not create cloud resources: the first deploy is the step that creates the environment, and merges after that update the repo. Crucially, everything runs in your own AWS account: the database, the cache, the load balancer, and the state behind them are yours, and you can revoke Vylara’s access and keep everything we created. Billing flows directly from AWS to you. If you’d rather not own a state backend at all, deploy without a DevOps team is the path. If you’ve already got a working remote backend and a team that’s comfortable with it, keep it — the S3-plus-DynamoDB pattern above is battle-tested and there’s no shame in running it yourself.

Either way, the principle doesn’t change. State must be shared, writes must be serialized, and a human should see the plan before production changes. Whether you implement that with a bucket and a lock table or let an agent handle it for you, those three rules are what keep collaborative infrastructure from quietly corrupting itself.

Try Vylara on your repo

Review your cloud plan in Vylara, merge delivery changes as Git PRs, and deploy into your own AWS or Azure account when you’re ready.

Start free

Frequently asked questions

Do I really need DynamoDB for state locking on AWS?
If more than one person or pipeline can apply against the same state, yes — DynamoDB is the standard AWS lock backend and prevents concurrent applies from overwriting each other. A pay-per-request lock table costs a few cents a month even with frequent use, so the only real cost is the one-time setup.
What does a remote state backend on AWS actually cost?
For a small team, well under $0.10/month. An S3 bucket holding a few hundred KB of state costs a fraction of a cent, and a pay-per-request DynamoDB lock table runs a few pennies even with frequent applies. The expensive part is operational time — bootstrapping the backend, writing CI gates, and clearing stuck locks — not the AWS bill.
What happens if an apply crashes and leaves the lock stuck?
The lock stays held until you clear it with a force-unlock command using the stuck lock ID. Only do this after confirming no apply is actually running, because force-unlocking a live apply reintroduces the exact race condition the lock exists to prevent.
Does Vylara make me set up an S3 bucket and lock table?
No. Vylara provisions and manages the AWS infrastructure in your own account, including the state and locking behind it, so there's no backend to bootstrap. You review every change as a diff and approve the first deploy, which is the step that actually creates the cloud environment.

Related posts