← All posts
Guide

Self-hosting TestVibe on your own infrastructure

Most teams should use the hosted cloud. TestVibe ships a self-hosted edition for the cases that need more than that: air-gapped environments, data-residency rules, or compliance requirements that rule out a hosted control plane. That's a different problem from keeping the app under test off the public internet alone — for that, a private runner is far less to operate (see below). This guide covers what actually runs where, how licensing works offline, and how to decide whether self-hosting is worth the operational weight.

Cloud, private runner, or self-hosted

These three options sit on a spectrum of how much you host, and they're easy to confuse.

OptionWhat you hostWhere your data lives
Cloud (SaaS)NothingTestVibe's cloud
Private runnerJust the runner imageApp in the cloud, test execution in your network
Self-hostedThe entire stackEntirely your infrastructure

The distinction that trips people up: a private runner is a feature of the cloud SaaS. You still sign in to the hosted app; only the test execution happens on a runner inside your network, so your app under test never has to be internet-reachable. Self-hosting is the opposite bet: you run the app itself.

If all you need is for test execution to stay inside your network, a private runner is less to operate than the full stack. A tunnel connects cloud execution to a private app; it does not move execution into your network. Reach for self-hosting when the whole product has to run on-prem, air-gapped deployments, strict data-residency rules, or compliance requirements that don't allow a hosted control plane.

What actually runs

Self-hosted TestVibe is delivered as a versioned Docker bundle, a docker-compose stack plus an .env template. A single docker compose up brings up a complete, private instance. The bundle is a handful of containers, each replacing a piece of the cloud platform:

ServiceRole
webThe TestVibe app (Wisej.NET), what users open in a browser.
postgresThe application database, holding accounts, projects, tests, runs, and canonical test files.
migrateA one-shot job that applies the database schema, then exits.
blobserverObject storage for artifacts, screenshots, videos, traces, and run bundles.
runnerExecutes your Playwright test runs.
browserA browser host the app drives for AI generation and the assistant.

The web app talks to Postgres for data, the blob server for artifacts, the runner to dispatch test runs, and the browser host to drive AI generation. Both the runner and the browser host need network reach to your site under test, so make sure your application's URL is reachable from inside the stack.

Compose enforces a strict startup order so the app never boots against an unprepared database: Postgres comes up healthy, the schema job runs and exits cleanly, the blob server comes up healthy, and only then does the web app start. Data lives in named Docker volumes, so it survives docker compose down / up and upgrades. Removing those volumes deletes everything, so back them up before any destructive operation.

Installing it

The container images are private; you authenticate to the registry with a token TestVibe provides before pulling. From there the flow is short and repeatable:

# after authenticating to the registry and filling in .env
docker compose up -d

# confirm it came up
docker compose ps

The app and object storage should report healthy, and the schema job should show Exited (0). Then open the app at the public URL you configured (the default is http://localhost:3000).

Upgrades are the same two commands against new image tags. The schema job re-runs and applies only its new changes, since it's idempotent, then the app restarts on the new version, and your data in the volumes is untouched.

Bootstrap configuration and administration

Use the release bundle's .env template for bootstrap settings. Settings → Administration is integrated into the web app for installation management; a separate customer-facing Admin service is no longer required. For secrets with documented {NAME}_FILE support, use a mounted Docker or Kubernetes secret.

You only supply the secrets and your public URL; the compose file already wires up which provider and internal URLs each service uses. The required values:

# The five values you must set
TV_PG_PASSWORD=             # PostgreSQL password, shared by postgres, migrate, and web
TV_BLOBSERVER_SIGNING_KEY=  # signs the short-lived artifact URLs
TV_BLOBSERVER_ADMIN_KEY=    # authenticates server-to-server calls to the blob store
TV_RUNNER_KEY=              # shared secret so only your app can dispatch runs
TESTVIBE_PUBLIC_BASE_URL=   # the URL users reach the app at; also OAuth + webhooks

Generate strong, unique secrets for each, treat them like production credentials rather than reusing template defaults. Everything optional (GitHub/Google sign-in, email delivery, connecting source repositories) is off until you set it, and the app still runs without any of it. Without email configured, verification and password-reset links are written to the app's logs instead of sent, which is fine for a closed internal instance.

One difference worth calling out: billing is off on-prem. The cloud SaaS meters session-seconds and AI tokens and bills through Stripe. On a self-hosted instance, credit gating and token metering are disabled: there are no credits to buy and nothing is metered. Licensing, infrastructure and any external provider usage remain costs and capacity controls.

The offline license model

A self-hosted instance is licensed with an offline, signed license. There's no phone-home, so it works fully air-gapped: the instance verifies the license locally against a key built into the app and does not require an outbound license-validation call; enabled model providers and integrations have separate network behavior. The license is bound to your specific install and names your organisation, so it can't be reused on a different instance, and it's perpetual by default.

You don't need a license to boot. The lifecycle has three states:

StateWhat happens
GraceNo valid license yet. The app runs normally and shows a warning. The window is a fixed number of days (default 14) and is persisted, so restarting doesn't reset it.
Read-onlyGrace expired with no license. Existing data stays fully viewable, but new test runs, generation, and load tests are paused until you activate.
LicensedA valid license is installed. Licensed capabilities run within the license edition and configured capacity; the warning clears.

Read access is never blocked: you can always sign in and view projects, tests, and results. Only new chargeable work pauses. To activate, use your self-hosted customer portal entitlement to obtain a signed license for the installation id, then apply it using the release's documented license configuration. If activation is unavailable, contact TestVibe with the installation id and organisation name. The instance picks it up on its next periodic check or restart. If a license lapses later, a fresh grace window starts before read-only kicks in, so renewals get a buffer rather than an instant cutoff.

Powering AI on-prem

Generation and the assistant require model access, as can scans, triage and optional AI plugins. Ordinary authored Playwright execution does not require the generation model. You have two options for where the model lives. Supply a hosted Anthropic or OpenAI key for a hosted-provider setup; evaluate quality and costs on your workload, at the cost of sending generation prompts to that provider. Or point TestVibe at your own OpenAI-compatible endpoint (Ollama, vLLM, LM Studio, or similar) so generation can stay inside your network when the endpoint, target site, and enabled integrations are internal. The bundle can even run a local model as an optional inference sidecar for a fully self-contained instance.

The one hard requirement: the model must support tool/function calling. TestVibe generates tests by having the model call browser tools step by step, so a model without tool-calling support will stall during generation. Choose a tool-calling-capable coding model, and plan on a GPU for acceptable speed.

Should you self-host?

Self-hosting trades operational simplicity for control. If your app under test is reachable from the internet, the cloud is genuinely less to run. If only execution needs to stay private, a private runner gets you there for a fraction of the effort. Reach for the full self-hosted stack when air-gapping, data residency, or compliance leave you no other option, then the offline license and local-model path can support an internal deployment when every enabled dependency and integration is configured internally. Read the full self-hosting docs at docs.testvibe.com, or get early access to try the hosted edition first.

Coming soon

Turn a described flow into a reviewable test.

Describe a flow in plain English. Generate a Playwright test, run it on your schedule, and review proposed fixes when your UI changes. Join the waitlist for early access.

  • Plain English in, real Playwright out
  • Runs in your CI, on your schedule
  • No credit card, unsubscribe anytime