Master ephemeral environments for pull requests using vcluster and GitHub Actions. Learn how to spin up isolated Kubernetes namespaces for faster testing.
If you’ve ever spent hours debugging a "works on my machine" issue that failed in staging, you know the pain of shared dev environments. They are bottlenecks. Someone deletes a configmap, or a migration goes sideways, and suddenly nobody can work.
I started using ephemeral environments to solve this. By spinning up a fresh, isolated Kubernetes cluster for every pull request preview, we eliminate contention. But spinning up a full EKS or GKE cluster for every PR is expensive and slow. That’s where vcluster changes the game.
Standard namespaces are fine, but they don't provide true isolation. If you need custom CRDs, different API versions, or cluster-wide permissions, a namespace won't cut it.
vcluster creates a virtual cluster inside your existing Kubernetes cluster. It’s lightweight, fast, and uses a fraction of the resources of a "real" cluster. Since it runs as a pod, you can spin one up in seconds.
We’ll use GitHub Actions to trigger the creation of a vcluster whenever a PR is opened.
vcluster CLI and helm in the runner.First, make sure you have the vcluster CLI installed in your runner. I prefer using the official setup action.
YAML# .github/workflows/ephemeral.yml name: PR Ephemeral Environment on: pull_request: types: [opened, synchronize, closed] jobs: vcluster-lifecycle: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Install vcluster run: curl -L -o vcluster "https://github.com/loft-sh/vcluster/releases/latest/download/vcluster-linux-amd64" && sudo install -c -m 0755 vcluster /usr/local/bin - name: Create vcluster if: github.event.action != 'closed' run: | vcluster create pr-${{ github.event.number }} --namespace pr-${{ github.event.number }} vcluster connect pr-${{ github.event.number }} --kubeconfig=./kubeconfig.yaml - name: Deploy App if: github.event.action != 'closed' run: | export KUBECONFIG=./kubeconfig.yaml kubectl apply -f k8s/deployment.yaml - name: Cleanup if: github.event.action == 'closed' run: vcluster delete pr-${{ github.event.number }}
When you move to this model, you'll hit a few hurdles. Here is how I handle them:
How do you access the app? I use ingress-nginx with a wildcard domain. Each vcluster gets a unique subdomain: pr-123.dev.example.com. You’ll need to configure your Ingress controller to route requests based on the host header.
Don't let a stray PR consume your entire node pool. Always set resource limits on the namespace where the vcluster runs.
YAMLapiVersion: v1 kind: ResourceQuota metadata: name: vcluster-quota spec: hard: requests.cpu: "2" requests.memory: 4Gi
If your app needs a database, don't spin up a managed RDS instance for every PR. It’s too expensive. Use a lightweight operator like Postgres Operator inside the vcluster or use a shared, pre-provisioned dev database instance.
I’ve seen teams try to implement this and fail because they forgot about clean-up. GitHub Actions are great, but if a workflow fails, the cleanup step might not run.
Pro-tip: Use a Kubernetes "Janitor" or a TTL controller. I use the loft CLI or a simple cron job that deletes any namespace prefixed with pr- that hasn't been updated in 24 hours. Never rely solely on the GitHub Actions lifecycle for infrastructure cleanup.
Integrating vcluster into your CI/CD pipeline is the single most effective way to improve developer velocity. It gives your team the freedom to break things without affecting their peers. It’s not just about cool tech; it's about removing the fear of deployment.
Start small. Try it on one microservice. Once you see the feedback loop tighten, you won't ever want to go back to shared staging environments.
Master ephemeral environments with vcluster and Loft to scale your devops workflow. Learn how to implement Kubernetes multi-tenancy for faster, isolated testing.
Read moreChaos engineering, Kubernetes resilience, LitmusChaos, fault injection, DevOps testing – learn practical steps, tool choices, and production‑ready examples to make your clusters fault‑tolerant.