Stage 7 · Master
Self-Service Platform API & CLI
Ephemeral Preview Environments
PR-based environments with TTL, namespace isolation, shared dependencies, and cost control. Integrate with GitHub Checks.
Why Ephemeral Environments?
Traditional staging environments are bottlenecks: shared, unstable, hard to reset. Ephemeral environments give each PR a production-like environment: isolated namespace, real dependencies, automatic cleanup. Developers test end-to-end before merge.
| Traditional Staging | Ephemeral Environments |
|---|---|
| 1 shared environment | 1 per PR (or per commit) |
| Manual deploy/reset | Auto-deploy on PR open/update |
| Flaky, contaminated state | Clean state every time |
| Queue to test | Parallel testing |
| Hard to debug integration | Real dependencies, real data |
| Cost: always running | Cost: TTL-based, auto-destroy |
Architecture
Lifecycle Management
- PR Opened → GitHub webhook → Platform controller creates namespace + ArgoCD Application
- ArgoCD syncs manifests from PR branch (or preview overlay) → deploys to namespace
- Crossplane provisions dependencies (DB, Redis, Kafka) scoped to namespace
- ExternalDNS creates
pr-<number>-<service>.preview.example.com - Platform posts comment on PR with preview URL + GitHub Check run
- PR Updated → ArgoCD auto-syncs new commit (self-heal + prune)
- PR Closed/Merged → Controller deletes namespace, ArgoCD app, Crossplane claims
- TTL Controller: If PR inactive > 24h (configurable), auto-destroy
Namespace Isolation & Shared Dependencies
Each preview gets its own namespace. But provisioning full DB/Redis/Kafka per PR is slow and expensive. Solution: shared dependencies with namespace-scoped access.
| Strategy | Description | Pros | Cons |
|---|---|---|---|
| Full Isolation | Provision dedicated RDS, Redis, Kafka per PR | True production parity | Slow (5-10 min), expensive |
| Shared DB, Schema Isolation | One RDS instance, per-PR schema + user | Fast, cheaper | Schema migration conflicts |
| Shared DB, Row-Level Security | One RDS, RLS policies per PR namespace | Fast, true isolation | Complex, PG only |
| Shared Redis/Kafka, Prefixed Keys/Topics | Single cluster, key/topic prefix = PR ID | Fast, cheap | Noisy neighbor risk |
| Mock/Stub Services | WireMock, LocalStack, Testcontainers for deps | Fastest, free | Not production-like |
Shared PostgreSQL with per-PR schema (fast, isolated). Shared Redis with key prefix pr-123:. Shared Kafka with topic prefix pr-123.. Mock external APIs (payment gateway, email). Provision real DB only for integration test stage.
GitHub Checks Integration
GitHub Checks API provides rich status in PR UI: preview URL, test results, deployment status. Platform creates check run on PR open, updates on deploy, completes on destroy.
Cost Control
- TTL: Default 4h for PR previews, 24h for draft PRs, configurable per team
- Resource quotas per namespace: CPU/memory limits, pod count, PVC storage
- Shared dependencies: One RDS/Redis/Kafka cluster serves all previews
- Spot/preemptible instances for preview worker nodes
- Auto-scale to zero: KEDA scales deployments to 0 when no traffic
- Cost attribution: Label all preview resources with
preview=true,pr=<number> - Budget alerts: Daily cost per team > threshold → alert + auto-destroy oldest
- Cleanup verification: TTL controller + finalizer ensures no orphaned resources
apiVersion: v1
kind: ResourceQuota
metadata:
name: preview-quota
namespace: pr-123-payment-api
spec:
hard:
requests.cpu: "2"
requests.memory: 4Gi
limits.cpu: "4"
limits.memory: 8Gi
pods: "10"
services: "5"
secrets: "20"
configmaps: "10"
persistentvolumeclaims: "2"
requests.storage: 10Gi
---
apiVersion: v1
kind: LimitRange
metadata:
name: preview-limits
namespace: pr-123-payment-api
spec:
limits:
- type: Container
max:
cpu: "2"
memory: 4Gi
default:
cpu: "500m"
memory: 512Mi
defaultRequest:
cpu: "100m"
memory: 128Mi
Quota prevents runaway previews. LimitRange sets sensible defaults.
Implementation Patterns
| Pattern | Description | Tools |
|---|---|---|
| ArgoCD ApplicationSet | Generate Application per PR from template | ArgoCD ApplicationSet generator |
| Flux Kustomization per PR | Flux creates Kustomization for each PR | Flux + GitRepository + Kustomization |
| Custom Controller | Platform controller manages namespace + ArgoCD App + deps | controller-runtime + ArgoCD API + Crossplane |
| GitHub Actions + K8s | Workflow creates namespace, deploys via kubectl/helm | GitHub Actions + kubectl/helm |
| Preview Service Mesh | Istio/Linkerd routes PR traffic to preview namespace | Istio VirtualService + header routing |
Previews use preview overlay (replicas=1, no PDB, relaxed resources, mock external APIs). They test integration, not load. Production uses production overlay (replicas=3+, PDB, HPA, real APIs).
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.