# How to Set Up Preview Deployments for Every Pull Request
The single biggest upgrade to a team's review workflow is giving every pull request its own live, shareable URL. Instead of "looks good to me" based on reading a diff, reviewers and stakeholders click a link and *use* the change. This guide explains how preview deployments work, the hard parts (data!), and how to set them up.
What a preview deployment is
When a PR is opened, the platform builds that branch and deploys it to a temporary, isolated environment with its own URL — often pr-123-myapp.example.com. Every push to the PR updates the preview. When the PR merges or closes, the environment is torn down. The result:
- Reviewers test real behavior, not just code.
- Designers and PMs see changes without a local setup.
- Bugs surface before merge, not after.
Vercel and Netlify popularized this for frontends; the pattern is just as valuable for full-stack apps — which is where it gets interesting.
The easy part: the app
Deploying the branch's code is straightforward on any Git-connected platform. The build runs the same way it does for production, just from the PR's branch. The challenge is everything *around* the app.
The hard part: data
A frontend-only preview is trivial. A full-stack preview needs a database, and that raises real questions:
| Strategy | How it works | Trade-offs |
|---|---|---|
| Shared dev DB | All previews hit one non-prod database | Simple, but PRs can corrupt each other's data |
| Per-PR ephemeral DB | Each preview gets a fresh database | Clean isolation; needs provisioning + seeding |
| Branched DB | Copy-on-write branch of a base DB | Best DX where supported |
For true isolation, per-PR databases are the gold standard: each preview gets its own database, seeded with representative data, and destroyed on teardown. The cost is automation — you need to provision, migrate, seed, and clean up programmatically.
Seeding realistic data
An empty database makes a useless preview. Seed each preview with enough data to exercise the feature:
# preview-setup.sh — run after the DB is provisioned
npm run db:migrate # apply schema
npm run db:seed -- --preview # load a representative fixture setKeep a curated, non-sensitive seed fixture in the repo. Never copy production data with real user PII into previews — that's a compliance landmine.
Environment variables per preview
Previews need their own config: the preview database URL, sandbox API keys (Stripe test mode, sandbox Twilio, etc.), and feature flags. The rule: previews must point at non-production external services so a test in a PR can never charge a real card or send a real SMS.
Setting up previews on PandaStack
- 1Connect your GitHub repo to an app in the [dashboard](https://dashboard.pandastack.io).
- 2Enable preview deployments per pull request, so each PR builds the branch (rootless BuildKit) and deploys to its own isolated environment with an HTTPS URL and automatic SSL.
- 3For data isolation, provision a database per preview and run your migrate + seed step on creation. PandaStack auto-wires
DATABASE_URL, so the preview app connects to *its* database automatically — no manual string juggling per PR. - 4Scope preview env vars to non-prod services (test-mode keys, sandbox endpoints).
- 5Rely on automatic teardown when the PR closes so you don't accumulate orphaned environments.
Why scale-to-zero fits previews perfectly
Preview environments sit idle most of the time — they're only used when someone clicks the link during review. PandaStack's free-tier scale-to-zero (KEDA) means an idle preview costs nothing and spins up on the first request. The cold start on that first click is a perfect fit for review traffic, where a second of warm-up is irrelevant. This is one of the few places where scale-to-zero is purely upside.
The review workflow it enables
With previews wired up, your PR flow becomes:
- 1Developer opens a PR → preview builds automatically.
- 2A comment/check posts the preview URL on the PR.
- 3Reviewers click, exercise the feature on isolated data, leave feedback.
- 4Pushes update the same preview.
- 5Merge → preview tears down; the change flows to staging/production.
This turns review from a code-reading exercise into a product-testing one, and catches whole classes of bugs (broken flows, layout regressions, bad migrations) before merge.
Pitfalls to avoid
- Sharing one database across previews — one PR's test data breaks another's. Isolate.
- Pointing previews at production services — a webhook test could mutate real data or charge real cards. Always sandbox.
- Copying production PII into seeds — use synthetic fixtures.
- No teardown — orphaned environments accumulate cost and clutter. Automate cleanup.
- Migrations that fail silently — surface migrate/seed errors so a broken preview is obvious, not mysteriously empty.
Cost and build minutes
Every PR push triggers a build, so previews consume build minutes (Free 300/mo, Pro 1000, Premium 2500). For busy repos, combine previews with change detection so unrelated pushes don't rebuild, and lean on scale-to-zero so idle previews cost nothing to *run*. The ROI is high: catching one bad migration before production pays for a lot of build minutes.
References
- [GitHub: About pull requests](https://docs.github.com/en/pull-requests)
- [Vercel: Preview deployments](https://vercel.com/docs/deployments/preview-deployments)
- [KEDA (scale-to-zero)](https://keda.sh/)
- [Twelve-Factor App: Dev/prod parity](https://12factor.net/dev-prod-parity)
Preview deployments turn code review into product review, and the make-or-break detail is isolated, seeded data per PR. PandaStack auto-wires a per-preview DATABASE_URL and uses scale-to-zero so idle previews are free. Set it up on the free tier at [dashboard.pandastack.io](https://dashboard.pandastack.io).