Prompt
How do I set up preview environments for my app?
Latest observation
Preview environments are temporary app deployments for every branch or pull request, so stakeholders can test changes before merge. A solid setup usually has 5 parts:
1) Choose the deployment model
Common options:
- Per-PR ephemeral environments: one environment for each PR; auto-create on open, auto-destroy on close.
- Branch previews: one environment per long-lived branch, like
staging-feature-x. - Hybrid: PR previews for most work, plus persistent
stagingandproduction.
For most teams, per-PR previews are the best default.
2) Make the app environment-aware
Your app should read config from environment variables rather than hardcoding values.
Typical variables:
APP_ENV=previewAPI_BASE_URL=...DATABASE_URL=...AUTH_CALLBACK_URL=...SENTRY_ENVIRONMENT=...
Also ensure your app can handle:
- dynamic base URLs
- generated secrets per environment
- CORS/redirect allowlists that include preview domains
3) Automate deployments in CI/CD
Set up your pipeline to:
- Detect PR opened/updated
- Build the app
- Provision dependencies if needed
- Deploy to a unique URL
- Comment the URL back on the PR
Example flow:
- PR #123 opens
- CI creates
preview-pr-123.yourapp.com - App is deployed there
- PR gets a comment with the preview link
- On PR close/merge, environment is deleted
Tools that help:
- GitHub Actions / GitLab CI / CircleCI / Jenkins
- Vercel / Netlify / Cloudflare Pages for frontend-heavy apps
- Kubernetes + Helm/Kustomize
- Terraform / Pulumi for infrastructure
- Docker + ECS/Fargate / Cloud Run / Fly.io / Render / Railway
4) Provision required dependencies
Preview environments often need their own:
- app instance
- database schema or isolated DB
- cache/queue
- object storage bucket or mocked storage
- feature flags
- third-party test credentials
You have three main patterns:
A. Fully isolated stack
Each preview gets its own DB and services.
- Best isolation
- More expensive
B. Shared services, isolated schema/database
One shared cluster, separate DB/schema per preview.
- Good balance of cost and isolation
C. Shared backend, mocked integrations
Frontend preview talks to staging or mocked APIs.
- Cheapest
- Less realistic
For apps with writes, avoid pointing previews at production databases.
5) Clean up automatically
Preview environments should not linger.
Add teardown for:
- PR closed
- branch deleted
- preview TTL expiry, e.g. 7 days
- inactive environment cleanup job
This prevents:
- cost leaks
- stale test data
- domain sprawl
Recommended architecture
A common setup:
- Frontend: deployed per PR to a unique subdomain
- Backend: either
- deployed per PR too, or
- shared staging backend with preview-specific config
- Database: isolated schema per preview
- Secrets: pulled from secret manager and scoped by environment
- DNS: wildcard subdomain like
*.preview.yourapp.com
Example URL:
pr-123.preview.yourapp.com
Important implementation details
Unique naming
Use a predictable environment name:
preview-pr-123preview-jdoe-login-fix
Branch safety
Prevent collisions when multiple commits update the same PR. Use the PR number as the stable identifier.
Secrets management
Don’t inject production secrets. Use a secret manager with per-environment values:
- AWS Secrets Manager
- GCP Secret Manager
- Vault
- Doppler
- 1Password Secrets Automation
Authentication/redirects
Update OAuth redirect URIs and callback URLs to support preview domains, or use a single auth proxy that works across envs.
Data seeding
Previews are more useful with seeded test data:
- demo users
- sample orders
- known states for QA
Cost controls
Add:
- TTLs
- resource quotas
- smaller instance sizes
- automated cleanup
Example GitHub Actions approach
High level:
name: Preview Deploy
on:
pull_request:
types: [opened, synchronize, reopened, closed]
jobs:
deploy:
if: github.event.action != 'closed'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./deploy-preview.sh pr-${{ github.event.pull_request.number }}
cleanup:
if: github.event.action == 'closed'
runs-on: ubuntu-latest
steps:
- run: ./destroy-preview.sh pr-${{ github.event.pull_request.number }}
Your deploy-preview.sh might:
- build the app
- create infra if needed
- set env vars
- deploy
- output the preview URL
Best practices
- Use previews for every PR
- Keep them close to production behavior
- Isolate stateful dependencies
- Automate teardown
- Comment preview URLs on PRs
- Keep deploys fast
- Use wildcard DNS if possible
- Add observability/logging per preview
Quickest path by stack
Static frontend
Use:
- Vercel
- Netlify
- Cloudflare Pages
These support preview deploys almost out of the box.
Full-stack app
Use:
- GitHub Actions or GitLab CI
- Docker
- Kubernetes or Cloud Run / ECS
- Terraform for infra
- per-preview DB schema or database
If you tell me your stack — for example Next.js on Vercel, React + Node on AWS, Rails, Django, Kubernetes, etc. — I can give you a concrete setup plan and example config.