Review environments — Victor Agnew

Victor Agnew

Staff AI Platform Engineer · Vancouver, BC

Boring deploys. work/ Safe AI. ai-lab/ Happy teams. virtual-office.md

PIPELINE
>20 min → <5 min
DEPLOYS
weekly → hundreds/week
FAILED PIPELINES
<1%
CREDENTIAL ROTATION
6 months → 24 h
REVIEW ENV
~$9/day
GCP SPEND
−40% YoY
MCP TOOLS
160+ → 23
VIRTUAL OFFICE
~1 h 40 min
virtual-office.md Virtual office three.js · Cloudflare Workers · LiveKit Built in a day; 26 days later, 1,600+ commits and 12 coworkers committing.
Close case-studies/review-environments.md Back

case-studies/review-environments.md

Review environments

Self-serve, full-stack review environments for any feature branch at about $9 a day, which grew into SSO-gated hosting for AI-generated reports.

ArborXR · Staff Platform Engineer

Full-stack review environment
~$9/day
Dedicated cluster, while running
$139–178/day
Cost difference
~15×
Teardown after the MR closes
automatic
  • vCluster
  • Kubernetes
  • Pulumi
  • FluxCD
  • Laravel
  • Filament
  • GitLab
  • Slack
  • Linear
  • MCP

The problem

The product stack includes a web app and API, an MQTT broker for devices, a docs site, Vault, Boundary, object storage, Postgres and Redis. Testing a feature branch against all of it takes a full environment. An environment built like production has its own cluster, network and managed database. In June 2026 I measured two of them at $139 and $178 a day while they ran.

The design

The internal developer platform started at KubeCon 2023, as an idea a colleague and I came up with. I was part of all of its initial planning, and much of the design was mine. It gives every feature branch a self-serve, ephemeral review environment that runs the full product stack in a vCluster, via Pulumi and FluxCD. A developer picks a branch in the web UI, or from an AI tool over MCP.

A vCluster per branch

Each environment is a vCluster: a virtual Kubernetes cluster with its own control plane, on one shared host cluster. It runs Postgres and Redis in-cluster and RustFS for object storage. Stateless services, such as the web app, workers and docs site, run on spot nodes. Each vCluster's control plane stays on on-demand nodes, because preempting it would disrupt the whole environment, and so do the databases and the MQTT broker. An environment can also run a review copy of the platform itself.

Two flavours, one program

The Pulumi program that builds production also builds every review environment. In review mode it creates a vCluster on the shared host cluster; otherwise it creates a dedicated GKE cluster. That second flavour, the integration environment, is built like production: its own cluster and VPC, AlloyDB and Cloud Storage.

Pulumi runs in a Job

Creating, updating or destroying an environment queues a Pulumi run. The platform's worker starts a Kubernetes Job for it and is free again in seconds. The Job runs Pulumi, records the result and retries up to three times on failure. It runs outside the platform's own release, so a platform deploy doesn't kill it mid-run. A reconciler checks every five minutes for runs whose pod died before it could report.

FluxCD deploys the branch

Pulumi installs FluxCD inside each environment. FluxCD watches the branch's own image repository, deploys the newest version and reports the deploy back to the platform through a webhook.

[ FIG. 1 ] From branch to environment, and back
From branch to environment, and back Left to right. Left: a "Feature branch" box. An arrow labelled "create (web UI or MCP)" to a "Developer platform" box. An arrow labelled "start Job" to a "Pulumi Job" box with a small note "one per environment and operation · up to 3 retries". An arrow labelled "pulumi up" to a large "Shared host cluster (GKE)" box holding three vCluster boxes side by side: "vCluster · branch A", "vCluster · branch B", "vCluster · branch C". Inside the first, small chips: FluxCD, web app + API, workers, MQTT broker, docs, Vault, Boundary, Postgres, Redis, RustFS. Along the bottom edge of the host cluster, two strips: "spot nodes: web app, workers, docs" and "on-demand nodes: control planes, databases, MQTT broker". An arrow from the FluxCD chip back to the platform labelled "deploy webhook". From the platform, two arrows up: to "Slack" ("started · done · failed") and to "Linear issue" ("deploy comment"). Bottom row, dashed: "Merge request closed" → "Daily cleanup (dry run to Slack first)" → "Pulumi destroy Job" → "vCluster · branch C", drawn dashed and labelled "deleted". Slack Linear issue started · done · failed deploy comment Feature branch create (web UI or MCP) Developer platform start Job Pulumi Job one per environment and operation · up to 3 retries pulumi up Shared host cluster (GKE) vCluster · branch A web app + API workers FluxCD MQTT broker docs Vault Boundary Postgres Redis RustFS deploy webhook vCluster · branch B vCluster · branch C deleted spot nodes: web app, workers, docs on-demand nodes: control planes, databases, MQTT broker Merge request closed Daily cleanup (dry run to Slack first) Pulumi destroy Job
Notifications and teardown

Deploy notifications go to Slack and Linear. Slack gets a message when provisioning or destruction starts, finishes or fails. When a new version deploys, the platform comments on the Linear issue named in the branch or its commits.

Environments tear down automatically after the merge request closes. By default, a daily job destroys every environment whose branch has no open merge request left, after a dry run posts the list to Slack. If GitLab can't be reached, the environment stays. The same cleanup covers integration environments, so their dedicated clusters go too.

Hosting what AI tools write

I conceived and led the platform's extension into an internal knowledge-sharing product: SSO-gated artifact hosting with versioning, inline comments and review workflows, exposed through an MCP server. I wrote its agent skill, so internal docs stop being shared as Markdown files, screenshots or text pasted into Slack.

An AI tool uploads a static HTML bundle over MCP and gets back a link that only the company's Google Workspace members can open. It's visible company-wide by default, or private behind a revocable share link, or a draft. Every publish keeps the previous version, which can be restored. Viewers leave Google-Docs-style comments that update live, and assigned reviewers approve or reject it.

I built its scheduled refresh. On a schedule, an agent run with no credentials of its own regenerates an artifact's data.json, so the page stays current. It reaches data through the same gateway as my zero-credential agents.

[ FIG. 2 ] Publishing from an AI tool
Publishing from an AI tool Left: an "AI tool" box with a chip "agent skill". An arrow labelled "upload over MCP" to a "Developer platform" box, and a return arrow labelled "link". Right of the platform: an "Artifact" card with a lock badge "Google Workspace sign-in" and a three-way switch "internal · private (revocable link) · draft". Behind the card, a stack of earlier versions "v1", "v2", "v3", with a curved arrow from "v1" to the front labelled "restore". Far right: a "Viewers" box with two arrows into the card, labelled "comments (live)" and "reviewers: approve / reject". Bottom, dotted: a "Scheduled refresh (agent run, no credentials of its own)" box with an arrow into a "data.json" chip on the card. AI tool agent skill upload over MCP link Developer platform v1 v2 v3 Artifact Google Workspace sign-in internal private (revocable link) draft data.json restore Viewers comments (live) reviewers: approve / reject Scheduled refresh (agent run, no credentials of its own)

Results

  • A full-stack review environment costs about $9 a day in its own compute and storage, roughly 15× less than a dedicated-cluster environment.
  • I measured the review figure with GKE cost allocation: the median of 90 full environment-days across 11 environments, September to October 2026. The dedicated figures come from the billing export, June 2026.
  • Developers and testers create environments themselves, in the web UI or through MCP.
  • Unused environments, and the clusters under them, are cleaned up automatically once their merge request closes.
  • AI tools publish reports and dashboards straight to versioned, SSO-gated pages with comments and reviews.
[ FIG. 3 ] Cost per environment per day
Cost per environment per day Horizontal bar chart on one linear scale from $0 to $180, ticks every $30. Bar 1: "Review environment (vCluster on the shared host cluster)", to $9, labelled "~$9/day". Bar 2: "Integration environment (dedicated cluster), while running": solid to $139 with a lighter extension to $178, labelled "$139–178/day (two environments)". A bracket between the bars labelled "~15×". Footnote in small mono text: "Review: the environment's own compute and storage, from GKE cost allocation, median of 90 full days, Sep–Oct 2026; excludes the shared host cluster's overhead. Integration: billing export, June 2026." Review environment (vCluster on the shared host cluster) ~$9/day Integration environment (dedicated cluster), while running $139–178/day (two environments) ~15× $0 $30 $60 $90 $120 $150 $180 Review: the environment's own compute and storage, from GKE cost allocation, median of 90 full days, Sep–Oct 2026; excludes the shared host cluster's overhead. Integration: billing export, June 2026.
next case study Unprivileged multi-arch builds