Imagine: a developer opens a PR with changes to an order form. QA waits for a link to test. The designer wants to see the button in context. The tech lead can't merge without a live review. Without preview environments, everyone waits for a deploy to staging, which someone else already occupies with another branch. In a typical team of 10 developers, each PR waits an average of 2 hours for a staging deploy. With preview environments, deployment takes 3 minutes. Our team of DevOps engineers has 7+ years of experience in CI/CD and Kubernetes. We've deployed preview environments for dozens of projects—from startups to enterprise. We'll help you automate PR testing. Get a project evaluation—just contact us.
Preview environments solve this bottleneck: each PR gets an isolated environment automatically, lives as long as the PR is open, and dies after merge or close. This reduces review time by 40%, cuts cloud infrastructure costs by 30% thanks to auto‑cleanup, and eliminates resource conflicts. Below, we break down how to build the system on Kubernetes and GitHub Actions, and which pitfalls to avoid.
Why Staging Doesn't Work for Parallel PRs
One staging environment for multiple active PRs is a classic problem. The feature/checkout-v2 branch overwrites bugfix/payment-redirect, the tester checks the wrong thing, the developer doesn't understand where the bug came from. Staging becomes a permanent resource conflict.
The second issue is dependencies. If the application uses a database, Redis, S3—each PR needs an isolated copy of those services, or at least a separate schema/namespace. Without that, tests from one PR corrupt data from another.
The third is lifetime. Stale PRs pile up, environments are not cleaned, and the cloud bill grows. Automatic cleanup on closed or merged events is essential.
How to Deploy Preview Environments on Kubernetes
A typical implementation on Kubernetes + GitHub Actions: when a PR is opened, a workflow is triggered that builds the image, pushes it to the registry with tag pr-{number}, applies a Helm chart in the namespace preview-pr-{number}, and comments on the PR with a link to the environment.
Example workflow
---
# .github/workflows/preview.yml (fragment)
on: pull_request:
types: [opened, synchronize, reopened, closed]
jobs:
deploy-preview:
if: github.event.action != 'closed'
runs-on: ubuntu-latest
steps:
- name: Deploy to preview namespace
run: |
helm upgrade --install \
preview-pr-${{ github.event.number }} ./helm/app \
--namespace preview-pr-${{ github.event.number }} \
--create-namespace \
--set image.tag=pr-${{ github.event.number }} \
--set ingress.host=pr-${{ github.event.number }}.preview.example.com
cleanup-preview:
if: github.event.action == 'closed'
runs-on: ubuntu-latest
steps:
- name: Delete preview namespace
run: kubectl delete namespace preview-pr-${{ github.event.number }} --ignore-not-found
For databases, there are several approaches. The cheapest is a separate PostgreSQL schema per PR: CREATE SCHEMA pr_519 AUTHORIZATION app_user;, migrations are applied, the schema is dropped after merge. No separate database pods needed. This works if the application supports search_path.
If full isolation is needed—ephemeral database via an operator (e.g., CloudNativePG with cluster templates) or Neon/PlanetScale with branch per PR.
DNS and Ingress
A wildcard certificate for *.preview.example.com plus a single wildcard Ingress rule via nginx-ingress or Traefik:
---
# ingress wildcard rule
- host: "*.preview.example.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: preview-router # routes by hostname to the correct service
Alternatively, cert-manager with Let's Encrypt DNS‑01 challenge issues the wildcard automatically.
Alternatives to Kubernetes
If you don't have a cluster, Render, Railway, and Fly.io natively support preview environments. Vercel and Netlify do it out of the box for static sites and Next.js. For heavier applications, Uffizzi or Namespace (formerly Shipyard) deploy environments in ephemeral clusters without you managing infrastructure.
A Docker Compose‑based approach: each PR runs docker compose up on a dedicated runner with port forwarding via Cloudflare Tunnel or ngrok. Cheap, but doesn't scale beyond ~10 concurrent PRs.
How to Avoid Database Conflicts in Preview
The most reliable method is to use separate PostgreSQL schemas for each PR. The application should dynamically select a schema via the environment variable SEARCH_PATH. This provides data isolation without spinning up additional containers. An alternative is ephemeral databases based on CloudNativePG: each PR gets a mini‑cluster that lives exactly as long as the namespace. This approach is more expensive but offers full instance‑level isolation.
What's Included in Setting Up Preview Environments?
We provide a complete turnkey package:
- Audit of your current infrastructure and CI/CD pipelines.
- Architecture design: database isolation strategy, DNS scheme, TTL policy.
- Implementation of Helm charts or Kustomize overlays for preview environments.
- Configuration of GitHub Actions / GitLab CI workflows with automatic deployment and cleanup.
- Integration of wildcard Ingress and TLS via cert-manager.
- Testing: simultaneously open 5 PRs, verify data isolation and cleanup.
- Documentation: runbook for on‑call, description for adding new dependencies.
- Team training and post‑deployment support.
Comparison of database isolation strategies:
| Strategy | Isolation | Cost | Complexity |
|---|---|---|---|
| Separate PostgreSQL schema | Medium | Low | Low |
| Ephemeral database (CloudNativePG) | High | Medium | Medium |
| External service (Neon/PlanetScale) | High | High | Low |
Timeline Guidelines
| Scenario | Duration |
|---|---|
| Static site / SPA (Vercel/Netlify) | 0.5–1 day |
| Monolith without DB, Docker Compose | 1–2 days |
| Service with PostgreSQL, Kubernetes | 3–5 days |
| Microservices, multiple dependencies | 1–2 weeks |
Pricing is determined after an audit: we need to understand your current CI/CD stack and infrastructure. The investment pays for itself through faster development—review time drops from 2 days to 4 hours.
Common Pitfalls and How to Avoid Them
Without automatic cleanup, dozens of dead namespaces accumulate in a month, increasing the cloud bill. Solution: set a TTL or cleanup on PR close event. A shared database without isolation leads to migration conflicts—schema‑per‑preview helps. Using production secrets in preview environments is unacceptable: a separate secrets store is needed. Without resource limits, one active PR can consume the whole node; 100m CPU and 256Mi memory suffice for preview. These measures ensure stability and cost savings.
Ready to implement preview environments? Get a consultation on your CI/CD—we'll analyze your current infrastructure and choose the best strategy. Contact us for an audit and project evaluation.







