platform-engineeringcost-optimizationgovernancecloud-waste

Environment sprawl: TTL policies that actually get enforced

Sprawl is not created by one bad decision, it accumulates from dozens of reasonable ones with no expiry attached. TTL policies are the cheapest fix available to a platform team, but only the enforced kind work. Here is how to design and run them.

The C3X Team··7 min read

Quick answer

Environment sprawl is cured by attaching an expiry to every non-production environment at creation and automating the teardown. Make TTL a required tag, default it to 7 days, warn the owner 24 hours before deletion, allow a bounded extension, and have the deleter run on a schedule with alerting when a destroy fails. At an average of 200 dollars per environment per month, cutting 40 stale environments recovers about 96,000 dollars a year.

Every organization that adopted ephemeral environments eventually discovers that "ephemeral" was aspirational. A count of running environments creeps from 12 to 40 to 90, and a meaningful share belong to branches merged months ago, demos that already happened, and experiments whose author has changed teams.

Nobody made a bad decision. Each environment was justified at creation. What was missing was an answer to "when does this go away", and without that answer the default lifetime is infinity.

What sprawl costs

Stale environmentsAt 200 dollars per monthPer year
102,000 dollars24,000 dollars
255,000 dollars60,000 dollars
408,000 dollars96,000 dollars
7515,000 dollars180,000 dollars

The 200 dollar figure assumes a small database, a couple of nodes, a load balancer, a NAT gateway, and observability. Kubernetes based environments sharing a cluster are cheaper per environment, often 30 to 80 dollars, but they sprawl faster because creation is trivial, so the totals land in the same range.

TTL is a creation time decision

The only moment when someone knows how long an environment should live is when they create it. Ask then. Make a ttl or expires_at value required on every non-production environment, whether it comes from a form field, a module variable, or a pull request label.

Default it to short. Seven days covers most feature work, and extending is cheap. A 30 day default sounds friendlier and produces sprawl, because a month is long enough for everyone to forget the environment existed. Write the value into a tag on every resource so the reaper can find it without a separate database.

The reaper has to actually delete

Policies that notify are not policies. The pattern that works is a scheduled job, hourly or daily, that lists resources by expiry tag, warns the owner 24 hours ahead with a one click extension link, then destroys on expiry using the same automation that created the environment.

Destroy correctness matters more than people expect. Partial teardowns leave the expensive parts behind: load balancers, volumes, elastic IPs, snapshots, NAT gateways. Alert on every failed destroy and treat it as a platform bug rather than a cleanup chore, or the leftovers become permanent. The specific offenders are covered in orphaned resource cleanup.

Extensions should be easy and bounded

If extending is hard, engineers route around the policy, usually by tagging things permanent or by creating environments outside the platform. Make extension a single click that adds 7 days, and allow it freely.

Bound the total though. Something like three extensions, or a 30 day hard ceiling, after which the environment must be converted to a managed long lived one with a named owner and a budget. The conversion step is the point: long lived infrastructure is fine, it just needs to stop pretending to be temporary and start being accounted for.

Different TTLs for different shapes

One policy for everything is too blunt. Preview environments tied to a pull request should be deleted when the pull request closes, no TTL needed, with a 3 day backstop for abandoned branches. Developer sandboxes suit a 7 day TTL with extensions. Demo and customer trial environments often need 30 days with a named owner. Shared integration environments are long lived and should be budgeted rather than reaped.

Encoding these as named policies in the platform, rather than as individual judgments, is what makes them survivable. The platform picks the policy from the environment type, and nobody negotiates per case.

Make the cost of sprawl visible

Reaping is easier to defend when the number is on a dashboard. Publish a weekly view of environment count by team, age distribution, and estimated monthly cost. Teams generally clean up voluntarily once they can see that their group holds 14 environments and 9 of them have not received a deployment in three weeks.

Estimated cost is the more useful column, and it does not require waiting for a bill. Because environments are created from Terraform, c3x can price each one from its plan with no cloud credentials, which gives you a per environment monthly figure the day it is created rather than 30 days later. Sum by owner and the dashboard writes itself, the same wayephemeral environment estimation works for individual pull requests.

Prevention beats reaping

Reaping cleans up after sprawl. Preventing it is cheaper. Environments that scale to zero when idle, share a cluster rather than claiming their own, use single-AZ storage, and skip dedicated NAT gateways cost a fraction of full clones, which means a stale one hurts far less.

A platform that combines cheap environment shapes, mandatory creation time TTLs, a reaper that genuinely destroys, easy but bounded extensions, and a visible cost dashboard will hold a steady state environment count that reflects actual work in progress. Without those pieces the count only goes up, and so does the line on the invoice that nobody can explain.

FAQ

What causes environment sprawl?

Missing expiry, not bad decisions. Each environment is justified when created, but if nobody answers the question of when it goes away, the default lifetime is infinity. Environments accumulate from merged branches, finished demos, and abandoned experiments until a count that started at 12 reaches 90, with a large share belonging to work that ended months ago.

How much does environment sprawl cost?

At a typical 200 dollars per month for an environment with a small database, two nodes, a load balancer, a NAT gateway, and observability, 40 stale environments cost about 8,000 dollars a month or 96,000 dollars a year. Shared cluster environments are cheaper per unit at 30 to 80 dollars, but they sprawl faster because creation is trivial, so totals land in a similar range.

What is a good default TTL for non-production environments?

Seven days. It covers most feature work and extending is cheap. A 30 day default sounds friendlier but produces sprawl, because a month is long enough for everyone to forget the environment exists. Preview environments tied to a pull request should be deleted when the pull request closes with a 3 day backstop, while demo environments often need 30 days plus a named owner.

How do I enforce a TTL policy that engineers will not route around?

Make TTL required at creation, run a scheduled reaper that warns the owner 24 hours ahead and then actually destroys, and make extension a single click that adds 7 days. Bound total extensions at roughly three or a 30 day ceiling, after which the environment converts to a managed long lived one with an owner and a budget. Hard extensions push people outside the platform.

How do I show teams what their environments cost?

Publish a weekly view of environment count by team, age distribution, and estimated monthly cost. Since environments are created from Terraform, C3X can price each one from its plan with no cloud credentials, giving you a monthly figure on day one rather than 30 days later. Summing by owner tag produces the dashboard, and teams usually clean up voluntarily once they see the numbers.

What to do next

Know what each environment costs on the day it is created. C3X prices Terraform plans with no cloud credentials. Try the quickstart.

Try C3X on your own Terraform

Free and open source. No API key required. One command to install, one command to estimate.