Why staging environments don't scale (and what to use instead)
A shared staging environment works fine when you have two engineers and one active branch. The moment the third engineer joins and two features are in review simultaneously, the queue forms. "Is staging free?" becomes a Slack message you send three times a day. Someone deploys over someone else's migration. QA flags a bug that turns out to be from a merged branch someone forgot to clean up. The staging environment — the thing that was supposed to speed up review — becomes the slowest part of the pipeline.
The problem isn't staging. The problem is sharing staging.
What breaks when you share a staging environment
The queue
Every feature branch needs staging at roughly the same time: right before review. If staging holds one version of the app, the second engineer has to wait. Waiting means context switching. Context switching means slower reviews, later merges, and a growing backlog of "ready for staging" branches that sit idle.
The collision
Engineer A deploys a branch that runs a database migration adding a status column. Engineer B deploys a branch that queries status but expects a different schema. Engineer B's preview breaks, but not because of Engineer B's code — because of Engineer A's migration running first. The team spends 20 minutes debugging a staging incident that has nothing to do with either feature.
The drift
Staging environments drift from production because they accumulate partial changes. A migration runs but gets reverted in prod. A feature flag stays on in staging but off everywhere else. An environment variable points at a stale service. The more the team uses staging, the less staging resembles production — which defeats the entire purpose.
The finger-pointing
"Staging is down." Three words that start a Slack thread with six participants, zero root causes, and one eventual finding: someone force-pushed to a shared branch. The outage wasn't a bug. It was a workflow failure.
Per-branch previews solve the sharing problem
Instead of one staging environment, every branch gets its own isolated URL. The queue disappears because there's nothing to queue for. Collisions disappear because Engineer A's branch runs in its own container with its own database connection. Drift shrinks because each preview starts from a clean build off the branch head. Finger-pointing disappears because the URL maps to exactly one commit.
This isn't a theoretical fix. Teams that move from a shared staging environment to per-branch previews report fewer deployment conflicts, faster review cycles, and a measurable drop in "staging is down" Slack messages. The workflow change is straightforward:
- Push a branch. The preview system detects the push, builds the container, and posts a URL to the pull request.
- Review the preview. Designers, PMs, and QA open the link in a browser — no deploy keys, no SSH tunnels, no VPNs.
- Merge and teardown. When the PR merges, the preview container stops. Nothing lingers. Nothing drifts.
Why "just spin up another staging server" isn't the answer
The obvious counter-argument: "We'll just run a second staging instance." That works for two branches. For ten? You're now maintaining ten staging servers, ten databases, ten sets of environment variables, and ten cleanup scripts. The operational cost of running multiple staging servers by hand is higher than the cost of the staging bottleneck itself.
A preview environment tool handles this automatically. You configure the build once — one Dockerfile, one set of base environment variables — and every branch inherits that configuration. The tool provisions the container, assigns the subdomain, tears it down after merge, and posts the URL to the PR. No server inventory spreadsheet. No forgotten instances running at $40/month for three months.
The database question
The most common objection: "What about the database?" Per-branch previews need data to be useful, and pointing every preview at a production database is obviously wrong. There are three approaches, in increasing order of isolation:
Shared dev database. All previews point at the same dev Postgres instance. Fastest to set up, lightest on infrastructure cost. Works if your branches don't run destructive migrations against each other. Not ideal for teams that frequently change schema.
Per-branch database branching. Services like Neon let you branch a Postgres database in under a second. Each preview gets its own branch — full isolation, no interference. Migrations run at container start and only affect that branch's data.
Seed data on deploy. Each preview starts with an empty database and runs a seed script. Predictable, clean, zero drift. Works well for apps where the schema is stable and the seed data covers the review surface.
Most teams start with a shared dev database and graduate to branching when the collision rate becomes annoying.
Does this replace CI?
No. Preview environments complement CI, they don't replace it. CI runs your tests, linters, and type checkers. A preview environment shows a reviewer what the branch actually looks like when it's running. CI says "the code is correct." The preview says "the app works." Both are necessary; neither replaces the other.
The workflow that most teams land on: CI runs on every push, tests pass (or you wouldn't bother previewing), and the preview deploys automatically after CI green. The PR comment includes the test status and the preview URL. Reviewers see both in one place.
Setting this up in under 10 minutes
If your app has a Dockerfile, you can get per-branch preview URLs today. Here's the outline:
# 1. Connect your repo (GitHub, GitLab, or Bitbucket)
# 2. Add environment variables (DB URL, secret keys)
# 3. Push a branch
git checkout -b feature/new-dashboard
git push origin feature/new-dashboard
# 4. Open the PR — the preview URL is in the comment
No infrastructure to provision. No YAML manifests. No Kubernetes namespace to reserve. The container builds from your existing Dockerfile, so what runs in the preview is what runs on your laptop — same dependencies, same runtime, same startup commands.
When shared staging is still fine
Per-branch previews aren't always necessary. If you're a solo developer shipping once a week, a shared staging environment is enough. If your team does most review asynchronously and rarely has two features ready for staging at the same time, the queue doesn't hurt you. The bottleneck only becomes real at around three engineers pushing regularly. Below that threshold, the operational simplicity of a single staging server outweighs the isolation benefit of per-branch URLs.
For every team above that threshold, per-branch previews stop being a nice-to-have and start being the thing that keeps review from being the bottleneck.
Next step
The free tier supports two concurrent previews — enough to try the workflow on a real project without committing to anything. If it works, paid plans start at $19/month per workspace, not per seat.
Ready to give every branch a live URL?
Free tier — 2 concurrent previews, no credit card required.
Start free