Running twins in hosted non-prod environments
Local twins on a developer laptop are step one. The next move is hosted non-prod — a staging environment, a preview deploy, or a shared dev host — where twins are reachable by the staging copy of your application without each engineer running wt up locally.
Two deployment models
Section titled “Two deployment models”A. In-process / per-job twins. Twins are spun up on-demand inside the same compute that runs the app (e.g. a docker-compose alongside your test runner, a sidecar in a preview deploy). Lifecycle is tied to the job/deploy.
- Best for: short-lived preview deploys, integration test runs, ephemeral environments
- Trade-off: every job pays the twin startup cost (~milliseconds, but it adds up); state isn’t shared between jobs
B. Long-running hosted twins. Twins run as persistent services on dedicated hosts, with stable URLs. Your staging app’s env vars point at those URLs (https://twins.staging.yourco.com/stripe, etc.).
- Best for: team-shared staging, manual QA environments, demos
- Trade-off: requires provisioning + reverse proxy + auth at the boundary; twins persist state across deploys
Most teams adopt B for shared staging and A for CI (see twins-in-ci.md).
Wiring it up
Section titled “Wiring it up”The application code from step 2 doesn’t change. The deploy pipeline sets the env vars to the hosted twin URLs instead of localhost:
# Generic deploy step (CI tool agnostic)- name: Build app for staging env: VITE_STRIPE_BASE_URL: https://twins.staging.yourco.com/stripe VITE_POSTHOG_HOST: https://twins.staging.yourco.com/posthog VITE_STRIPE_KEY: ${{ secrets.STAGING_STRIPE_KEY }} run: pnpm buildProduction deploys leave those _BASE_URL vars unset → SDK falls back to real vendors.
Reference architecture: WonderTwin’s own dogfood
Section titled “Reference architecture: WonderTwin’s own dogfood”WonderTwin runs hosted twins behind a caddy reverse-proxy at api.wondertwin.dev/twins/<vendor> (e.g. api.wondertwin.dev/twins/posthog, api.wondertwin.dev/twins/logodev). The staging deploy of wondertwin-web sets:
VITE_WT_POSTHOG_HOST: https://api.wondertwin.dev/twins/posthogVITE_WT_LOGODEV_BASE_URL: https://api.wondertwin.dev/twins/logodevThe production deploy of the same workflow leaves those unset, and the apps fall back to real us.i.posthog.com and img.logo.dev respectively. Single codebase, single deploy template, environment-determined behavior.
Debug surface (recommended)
Section titled “Debug surface (recommended)”Add a /admin/services (or similarly-gated) page to your app that lists every service in your registry, the URL it resolved to, and twin-or-real status. Two reasons:
- In staging, you can verify the twin layer is actually wired before doing manual QA — no more “I thought I was hitting the twin but my staging API key got billed.”
- The page surfaces misconfigurations (missing env var, typo’d service name) that would otherwise be invisible.
Gate this behind admin auth or a build-time flag — it’s a development affordance, not a production surface.
TODO / coming later
Section titled “TODO / coming later”- Concrete provisioning patterns (Kubernetes Deployments, Cloud Run services, plain systemd, Fly machines)
- Reverse-proxy / cert / auth patterns at the twin boundary
- State persistence between deploys (when twins should be ephemeral vs. durable)
- Resource sizing guidance
- Multi-region staging considerations
- Cost model — how much hosting twins typically costs at staging scale