# How It Works

URL: https://docs.testvibe.com/self-hosting/how-it-works

A self-hosted TestVibe is a small set of containers orchestrated by Docker Compose. Each one replaces a piece of the cloud platform with something you run yourself.

## The Services

| Service | Role | Notes | **web** | The application — the Wisej/.NET app users open in a browser. | Configured entirely from the environment; no config file. | **postgres** | The application database. | Holds accounts, projects, tests, runs, and canonical test files. | **migrate** | One-shot schema job. Applies the database schema, then exits. | Runs again on every start; it is idempotent, so already-applied changes are skipped. It also handles **upgrades** — a new release applies only its new schema changes. | **blobserver** | Object storage for artifacts, screenshots, videos, traces, and run bundles. | The on-prem replacement for cloud blob storage. Hands the UI and runner short-lived signed URLs. | **runner** | Executes your Playwright **test runs** . | The app dispatches each run here; it reaches the blob server and the site under test over the network. | **browser** | A browser host for **AI feature generation** and the assistant. | The app drives it in-process and assembles the generated test itself. Needs an [AI key or local model](/self-hosting/ai-models) . 

## How They Connect

```text
┌─────────┐
  user ───▶ │   web   │ ──────────────┐
            └────┬────┘               │
                 │                    ▼
       ┌─────────┼──────────┐   ┌──────────┐
       ▼         ▼          ▼   │  runner  │ ──▶ your app under test
 ┌──────────┐ ┌──────┐ ┌────────┐  (test runs)
 │ postgres │ │ blob │ │ browser│ ──▶ your app under test
 └──────────┘ │server│ └────────┘  (AI generation)
              └──────┘
```

- The **web** app talks to **postgres** (data), **blobserver** (artifacts), the **runner** (dispatching test runs), and the **browser** host (driving AI generation).
- The **runner** and **browser** both need network reach to the **site under test** and to the blob server. Make sure your application's URL is reachable from inside the stack.
- Users reach the **web** app at the public URL you configure — this is also where OAuth callbacks and webhooks arrive.

## Startup Order

Compose enforces a strict order so the app never starts against an unprepared database:

1. **postgres** comes up and reports healthy.
2. **migrate** runs the schema job and exits successfully.
3. **blobserver** comes up and reports healthy.
4. **web** starts — only after the three above are ready.
The **runner** and **browser** are always-on services that the app calls when it needs them.

## Data And Persistence

The database and object storage write to **named Docker volumes** , so your data survives `docker compose down` / `up` and upgrades. Tearing the stack down *with volumes removed* deletes it — back up the volumes before any destructive operation.

## Related Help

- [Install](/self-hosting/install)
- [Configuration](/self-hosting/configuration)
- [AI models](/self-hosting/ai-models)
