2026-01-03 | PreviewProof Team

Cost-Aware Preview Environments: Auto-Sleep, Right-Sizing, and Cleanup Patterns

preview environmentscost optimizationinfrastructureFinOpsephemeral environments

The first reason teams give up on preview environments is reliability. The second is cost. Both are solvable, and both get worse if you ignore them. A team running 50 PRs a week with multi-service previews left up 24/7 will spend more on previews than on production within a quarter. They’ll notice when the AWS bill arrives, kill the previews, and complain that previews are too expensive — when what was actually too expensive was the specific way they ran them.

The math works when previews are appropriately ephemeral, appropriately sized, and appropriately cleaned up. None of those happen by accident.

The cost shape

Production is sized for peak load. Preview is sized for one reviewer clicking around. The difference is enormous, and most teams under-exploit it.

A typical small-team preview, naively provisioned:

  • 3 application containers (web, worker, api) at 1 vCPU / 2GB each
  • 1 Postgres at 2 vCPU / 4GB
  • 1 Redis at 0.5 vCPU / 1GB
  • 1 load balancer
  • ~50GB attached storage

At list AWS prices, that’s around $180/month per preview running continuously. Multiply by 50 active previews and you’re at $9,000/month — for environments actively used for maybe an hour each before merge.

The same workload, sized appropriately and shut down when idle, costs $15-25 per preview-month. That’s the entire game. From $180 to $20 is the gap between “we can’t afford previews” and “previews are a rounding error.”

Auto-sleep is the single biggest lever

Most preview environments are idle 95% of the time. The reviewer opens the URL, clicks around for 15 minutes, walks away. The environment sits burning CPU minutes for the next 36 hours until merge.

The pattern: route preview traffic through a proxy that tracks last-access time. After N minutes without requests, scale the preview’s compute to zero. When a request comes in, scale back up before forwarding. The user sees a 5-30 second wake-up delay on first access, then normal performance.

Implementations vary. Fly.io’s auto_stop_machines does this natively. Render and Heroku review apps have it built in. Kubernetes-based platforms typically use KEDA or a custom controller.

The constraint: not everything wakes from zero gracefully. Stateful services usually shouldn’t sleep — they’re cheap idle and waking them is risky. Sleep the application containers. Keep the data layer warm.

Right-size aggressively per stack

Reviewer load is one user, occasionally two. The container sizes that make sense for production traffic are 5-10x larger than they need to be in preview.

A reasonable starting point:

  • Rails / Django / Laravel: 0.25 vCPU, 512MB. Boots in 10-30 seconds. Plenty for one reviewer.
  • Node / Next.js / Express: 0.25 vCPU, 256-512MB. Less if not server-rendered.
  • Postgres: 0.25 vCPU, 512MB. Seed data is megabytes, not gigabytes.
  • Redis: 0.1 vCPU, 128MB. Reviewers don’t generate cache pressure.
  • Background worker: 0.25 vCPU, 512MB.

The cost difference between 1 vCPU and 0.25 vCPU compounds. On Fargate, the smaller container is 4x cheaper. On Kubernetes, you pack more previews per node.

Exceptions: builds and migrations. Give the build container 4 vCPU, then drop to 0.25 for serving. Don’t right-size the migration step — give it headroom and dispose. The serving footprint is what you’re trying to shrink.

Clean up on close. Then on staleness.

Every platform supports “destroy when PR closes.” Most teams enable this and stop there.

PRs get abandoned. Branches get force-pushed. Reviewers leave the company. Without a staleness policy, you accumulate zombie previews — paid for, never accessed.

A reasonable policy:

  • Destroy on PR close (merge or abandon). Default true.
  • Destroy after 14 days of no PR activity, even if open. Notify the author 24 hours in advance.
  • Destroy after 30 days regardless. No exceptions without explicit opt-in.
  • For drafts, shorter timeouts — 7 days.

Implement as a scheduled job that queries your platform’s preview list, cross-references PR activity, and destroys the stale ones. Make the destruction visible — post a comment on the PR so engineers don’t think the preview broke.

Pool what’s expensive, isolate what matters

The temptation when costs spike is to share infrastructure between previews. This is a trap, but a survivable one if you’re disciplined.

Don’t share the primary database. See Per-Preview Database vs. Shared Dev Database. Cross-contamination of test data destroys trust in previews.

Don’t share the Redis used for queue work. See Background Jobs in Ephemeral Preview Environments.

Do share expensive services that don’t hold per-preview state. A single OpenSearch cluster with per-preview indexes is fine. A shared Kafka with per-preview topics is fine. A shared S3 bucket with per-preview prefixes is fine. Rule: shared resource is OK if the namespacing is enforced at the resource level, never by application code remembering to scope queries.

Do share read-only third-party integrations. No benefit to a per-preview Stripe test account. Use the same test mode credentials across previews — the test mode itself provides isolation.

A realistic cost example

8 engineers, 50 PRs per week, average preview lifetime 36 hours, average active time 90 minutes:

ComponentAlways-onAuto-sleep + right-sized
App containers (3)$90/mo$4/mo
Postgres$30/mo$8/mo
Redis$10/mo$3/mo
Storage$5/mo$2/mo
Load balancer share$5/mo$1/mo
Total per preview$140/mo$18/mo

At 50 active previews simultaneously: $7,000/month vs. $900/month. The actual numbers depend on your provider — Fargate is pricier than EC2 spot, Cloud Run is cheaper than App Engine — but the ratio holds. Auto-sleep and right-sizing get you 5-10x.

The contamination trap

Cost concerns drive teams toward shared infra, which causes contamination bugs, which destroys trust, which kills the preview workflow. Sharing a database between two previews saves maybe $5/month. The cost of reviewer A’s test data showing up in reviewer B’s preview is four hours of debugging and one engineer who now refuses to use previews. Pool what’s safe to pool. Isolate what isn’t.

What this gets you

Done right, previews cost roughly one production-size environment per 50 active previews — 2-5% of your production infra spend. The math gets better as the team grows because per-preview costs stay flat while review velocity scales. Done wrong, previews cost more than production and get killed in the next budget review.

If you’d rather not build the auto-sleep proxy, right-sizing defaults, staleness reaper, and shared-pool isolation yourself, that’s most of what we provide at PreviewProof. Auto-sleep on by default. Right-sized per stack. Staleness cleanup with notifications. The cost numbers above are roughly what we target. Build it yourself if you want — patterns are above — or skip the work.