Skip to main content

vCluster vs Signadot vs Full-Stack Copy: Which Preview-Environment Model Fits a Self-Hosted Fleet's Fixed Capacity?

11 min readDora NodaDora Noda
Share
On this page

Every pull request deserves a live preview. The question is what you duplicate to build it: the whole stack, a whole cluster, or just the changed service. On elastic cloud budgets the answer barely matters — idle capacity absorbs the waste. On a self-hosted fleet with a fixed number of nodes, the isolation model you pick is your preview-environment budget, and the wrong one means either buying another machine or rationing previews to a handful of concurrent PRs.

Here is the short version, worked out in full below for a typical 8-service app on a fixed 3-node fleet. Full-stack copy costs roughly one whole app footprint per PR and caps out at single-digit concurrent previews. Virtual clusters (vCluster) cost one app footprint plus a small control-plane tax per PR, and earn it back only when PRs touch cluster-scoped resources. Request-level routing (Signadot) costs roughly one service footprint per PR, stretching the same hardware an order of magnitude further.

If you run a self-hosted PaaS on owned machines, request-level isolation should be the default, vCluster the escape hatch, and full-stack copy the thing you grow out of.

ModelWhat each PR duplicatesPer-PR footprint (8-service app)Concurrent PRs on reference fleet*Startup time
Full-stack copy (Okteto-style namespace)Every service + data~2 vCPU / 4 GB + database~4–6Minutes
Virtual cluster (vCluster)Every service + a control plane~2 vCPU / 4 GB + ~0.1 vCPU / 0.25 GB overhead~4–6Seconds
Request-level routing (Signadot Sandbox)Only changed services~0.25 vCPU / 0.5 GB per changed service~40–60Seconds

*Reference fleet: 3 nodes × 4 vCPU / 16 GB with ~60% allocatable to previews; assumes one changed service per PR for the routing row. The math is below — including the database line item most comparisons skip.

The three architectures in one page

Full-stack copy is the oldest and simplest model: every PR gets its own namespace with a complete deployment of the application. Okteto's preview environments are the canonical example — a server-side clone of the branch deployed on PR open, a shareable URL for reviewers, automatic teardown on close, wired into CI with the okteto/deploy-preview GitHub Action. The mental model is unbeatable: the preview is the app, just pointed at a different branch.

The cost model is equally simple and equally brutal: infrastructure spend scales linearly with services × PRs. Eight services and five open PRs means running your application six times (production plus five copies). Industry analyses of ephemeral environments routinely find non-production estates costing three to five times production — full-stack copy is how you get there.

Virtual clusters give each PR its own Kubernetes control plane — its own API server, its own RBAC, its own CRD scope — running as pods inside a namespace on the shared host cluster. vCluster, the open-source (Apache 2.0 core) project from vCluster Labs (formerly Loft Labs), is the reference implementation: virtual clusters spin up in seconds with a single command, and the per-cluster control-plane overhead for a small cluster is commonly measured around 50–100 millicores of CPU and 128–256 MB of RAM. That buys the strongest isolation of the three models: a PR that installs an operator, migrates a CRD, or needs cluster-admin can run safely because it genuinely has its own cluster.

The commercial Pro tier adds niceties like sleep mode for idle virtual clusters. What it does not buy is cheap previews for ordinary app PRs — the workload pods are still duplicated in full, so the per-PR footprint is full-stack copy plus the control-plane tax.

Request-level routing deploys only the changed services onto a shared baseline cluster and routes traffic with headers. Signadot's Sandboxes are the defining implementation: the baseline runs the main branch, each PR deploys just its diff, and a routing layer (service mesh or sidecar-based) sends header-tagged requests to the sandbox version while everything else falls through to baseline. Previews spin up in seconds, marginal cost per PR is roughly one service instead of one stack, and Signadot's own comparison literature puts the scaling ceiling in the hundreds of concurrent environments. The tradeoff is architectural rather than financial: your cluster needs the routing layer, and services must propagate routing context for multi-hop calls.

Worked capacity math on fixed hardware

On owned hardware there is no per-second meter — there is a ceiling. Cost is measured in PR slots: how many concurrent previews fit before you must buy another node. Take a concrete reference: three 4-vCPU/16-GB nodes, ~60% of capacity allocatable to previews after production, system overhead, and headroom. That leaves roughly 7 vCPU and 29 GB for previews.

Assume a typical 8-service app where each service requests ~250 millicores and 512 MB. A full application copy needs ~2 vCPU and 4 GB.

  • Full-stack copy: each PR consumes one full copy — ~2 vCPU / 4 GB. The reference fleet fits 4–6 concurrent PRs before previews start evicting each other. The sixth open PR on a busy Monday is the one that pages someone.
  • vCluster: each PR consumes one full copy plus ~0.1 vCPU / 0.25 GB of control-plane overhead — ~2.1 vCPU / 4.25 GB. Same ceiling, 4–6 concurrent PRs, slightly worse. The control-plane tax is real but small (~5%); the workload duplication dominates.
  • Request-level routing: each PR consumes only its changed services. At one changed service per PR — the common case — that is ~0.25 vCPU / 0.5 GB, and the same fleet fits 40–60 concurrent PRs. Even at three changed services per PR, the ceiling is still ~15.

Sensitivity matters more than any single row, because the variable that drives the result is concurrent PRs, not services:

Concurrent PRsFull-stack copyvClusterRequest-level (1 svc/PR)
2fits easilyfits easilyfits easily
5tight, ~fulltight, ~full~10% of capacity
20needs ~4× the fleetneeds ~4× the fleetfits easily

Below ~4 concurrent PRs the models are indistinguishable on capacity — pick by simplicity. Above that, full duplication hits a step function: every ~5 more PRs demands another node (another monthly box on the invoice, another machine in the Cluster API fleet to provision, patch, and monitor). The routing model absorbs the same growth inside existing capacity. That is the entire economic argument in one sentence: on fixed hardware, duplication-based previews convert PR concurrency into machine purchases.

The database question everybody skips

Stateless services are the cheap half of a preview. Data is where full-copy math usually cheats: either each preview shares the baseline database (fast, cheap, and one bad migration away from corrupting everyone's testing) or each preview gets its own database (correct, and suddenly your per-PR footprint includes a Postgres plus its storage).

Name the options honestly per model:

  • Full-stack copy pushes you toward a database per preview for real isolation — ephemeral Postgres instances with copy-on-write clones or seeded dumps. That is the correct setup and it roughly doubles the per-PR footprint computed above once storage and memory for the database are included. Teams that share one database across previews to dodge this cost rediscover why isolation existed the moment a migration PR drops a column.
  • vCluster has the same choice with the same costs — the virtual control plane does not virtualize your data layer. Full data isolation per virtual cluster is available and priced accordingly.
  • Request-level routing offers tunable data isolation: route to shared baseline data by default, spin up preview-scoped data only for the services under test or the migration under review. This is the model's second cost advantage after compute, and arguably the larger one — most PRs genuinely do not need their own database, and the routing model is the only one where "most PRs share data, migration PRs get a clone" is a supported configuration rather than a corner you cut.

Any preview-environment cost comparison that prices only stateless pods is flattering duplication-based models by omitting their most expensive line item. Include the database and the gap widens, not narrows.

What each model cannot test

Cost decides the default; fidelity decides the exceptions. Each model has a class of change it fundamentally cannot preview:

  • Request-level routing cannot preview changes to cluster-scoped resources — CRDs, operators, admission webhooks, service-mesh config, or the routing layer itself. It also assumes services propagate routing headers across hops; a service that drops context breaks sandbox isolation for everything downstream of it. If your PR is the infrastructure, routing-based previews test everything except the PR.
  • Full-stack copy handles app-level changes faithfully but shares the host cluster's control plane, so cluster-scoped changes are still off-limits (or dangerous — one preview's CRD migration touches every namespace). It also drifts from production in the ways that matter at scale: a single-copy namespace does not reproduce production's replica counts, autoscaling behavior, or multi-AZ topology.
  • vCluster is the only model that previews infrastructure PRs faithfully: operators, CRD migrations, RBAC changes, and Helm chart rewrites run against a real, isolated control plane. Its fidelity gap is operational rather than architectural — virtual clusters must track the host's Kubernetes version and CNI behavior, and a preview that passes on a version-skewed virtual cluster proves less than it appears to.

This is why the answer is a default plus an escape hatch rather than a single winner. Route ordinary app PRs through request-level sandboxes; spin up a virtual cluster for the operator upgrade, the CRD migration, and the chart rewrite. The expensive isolation should be opt-in per PR, not the price of every typo fix.

The verdict for a self-hosted PaaS

If you are building preview environments into a Cluster-API-managed platform on owned machines, the capacity math above makes the decision:

  1. Default to request-level isolation. It is the only model whose per-PR cost tracks the size of the change rather than the size of the app, which is exactly the property a fixed-capacity fleet needs. The routing layer (mesh or sidecar) is real operational complexity, but it is one shared component to operate, versus N full app copies to schedule, observe, and clean up.
  2. Offer virtual clusters as the privileged tier. Infrastructure PRs are rare and high-blast-radius — precisely the workload where vCluster's seconds-to-provision, strongly isolated control planes earn their overhead. Gate it behind a label (preview: full-isolation) rather than making it the default.
  3. Skip namespace full-copy as the platform primitive. It has the simplest mental model and the worst cost curve on hardware you cannot autoscale away. Teams that outgrow single-box self-hosting hit this wall first: the model that felt free on one idle server becomes a machine purchase per sprint at fleet scale.

Two operational footnotes apply regardless of model. First, automate teardown ruthlessly — orphaned previews that survive their PR are the fastest route to a fleet that is technically sized correctly and mysteriously always full. Second, put previews on sleep or scale-to-zero when idle; vCluster's sleep mode exists because everyone who runs previews at scale learns that a preview which nobody is clicking is pure waste heat.

The broader pattern is worth naming: preview environments are where "just run another copy" meets finite hardware. Cloud-native teams learned to solve it with elastic budgets; self-hosted teams have to solve it with smarter isolation. That is not a disadvantage — it is a forcing function toward the architecture that scales further on any substrate. A platform that can preview a hundred PRs on three owned nodes has built something its cloud-billed competitors will eventually need too.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Preview environments are on the roadmap, and this cost model is informing the design. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex