Overview
This section covers standing up The Test Cabinet’s two long-running services
— the backend (tcab-backend) and the
worker (tcab-worker) — as remote
environments: a staging and a production environment on
Azure. The guidance is written to be reproducible by anyone running their own
instance; there is nothing here that is specific to a private deployment.
To run the same services entirely on one machine for development — the local mirror of everything below — see Running in the Development section. This section mentions the local shape where it helps explain the remote one, but its emphasis is the real, remote build.
For the static surfaces — the public gallery, this docs site, and the per-run playable builds — see Releasing instead. Those are built in CI and need no servers. This section is only about the services that do.
What gets deployed
Section titled “What gets deployed”| Thing | Deployed as | Covered by |
|---|---|---|
Backend (tcab-backend) | A long-running HTTP service | This section |
Worker (tcab-worker) | A long-running HTTP service on a host with a container runtime | This section |
Web console (apps/web) | A static bundle served to operators on the private network | This section |
| Gallery, docs, per-run builds | Static sites built in CI | Releasing |
CLI (tcab), Tauri app | Local tools an operator installs | Not deployed — see Building |
The CLI and Tauri app are runner/reporter tools that an individual operator runs on their own machine; they are not part of a deployment. The web console is part of one, but it is just a static bundle — the only stateful, always-on processes to operate are the backend and the workers.
Environments
Section titled “Environments”The same two binaries run in every environment; what changes is where they bind,
what they talk to, and how they are kept up. The custom TCAB_ENV variable tags
each one (local, staging, prod) so telemetry
and logs from each can be told apart.
| Environment | Purpose | Backend | Workers |
|---|---|---|---|
| Local | Exercise the whole flow on one machine (development) | A process (or container) on localhost | A process on the host, using the host’s container runtime |
| Staging | A production-shaped environment to validate changes | Managed (Azure Container Apps) | One or more VM nodes |
| Prod | The environment operators actually use | Managed (Azure Container Apps) | A pool of VM nodes |
The local environment is a development convenience and is documented under
Running, not here. This section is about the two
remote environments: staging and prod are the same topology — keep them
identical so staging is a faithful rehearsal — differing only in scale, their own
secrets, and their TCAB_ENV tag. See Azure: staging & prod.
Two runtime shapes
Section titled “Two runtime shapes”The backend and the worker have very different hosting needs, and that difference drives every choice in this section.
- A worker host needs a real container runtime. Each run executes inside a fresh container the worker starts itself (see Execution and Run Containers). Running a worker therefore means running Docker- or Podman-in-a-container, so a worker belongs on a VM (or any host with a normal container runtime), not on a serverless container platform that forbids nested/privileged containers.
- The backend is a (mostly) stateful service with no container runtime. It
keeps a database, an on-disk definition store, and a repository checkout it
ingests from, and it renders reference screenshots with a headless browser at
ingest. With its default embedded SQLite store it runs on a managed
container platform provided it is pinned to a single replica (SQLite is
single-writer) with a persistent volume and an image that includes a
browser. Pointing
TCAB_BACKEND_DATABASE_URLat a managed PostgreSQL instead lifts the single-replica and database-volume constraints. The details are in Azure: staging & prod.
| Service | Container runtime on host? | Persistent storage | External egress |
|---|---|---|---|
| Worker | Yes — runs each test case in a container | Scratch only (TCAB_WORKER_OUT_DIR, TCAB_WORK_DIR) | Model APIs + package registries (from inside run containers); GitHub & Cloudflare when it publishes |
| Backend | No | Yes — database (SQLite, or external PostgreSQL), definition store, ingest checkout | Cloudflare R2 (snapshot upload) + the site’s deploy hook |
Access: there is no app-level auth
Section titled “Access: there is no app-level auth”Neither service has accounts, tokens, or a login. As described under Backend authentication, the model is that reachability is the access control: both services bind to a private address and are never exposed to the public internet, so only machines and people who can already reach them on a private network can use them.
That has one consequence worth stating up front, because it shapes the worker
topology: a worker’s jobs are held per-instance —
POST /runs returns a job id you then poll on the same worker, and the
web console adds workers by URL, one at a time. A
worker “pool” is therefore a set of individually addressable hosts, never a
single load-balanced endpoint. Giving each worker its own stable private address
is exactly what a mesh VPN does for free.
Two ways to provide that private network are documented, and you can pick either:
- Tailscale (or a comparable mesh VPN) — the simple, portable default. Each
service gets its own stable
100.xaddress on your tailnet, which suits the per-worker addressing above and is identical whether a host is on Azure, on another cloud, or under your desk. This is what the.envexamples already assume. - An Azure-native private network — a VNet with the services on private subnets, reached through a VPN gateway or Azure Bastion. No third-party dependency, at the cost of more setup and being Azure-specific. Covered as an alternative in Azure: staging & prod.
Secrets and telemetry
Section titled “Secrets and telemetry”- Secrets — harness API keys, the
GITHUB_TOKENand Cloudflare token used when a worker publishes, and the backend’s R2 credentials and deploy-hook URL — are supplied through the environment or your platform’s secret store and are never committed. Every file underdeployments/is an.example/placeholder template, matching the repo-root.env.backend.exampleand.env.worker.example, which remain the authoritative reference for every variable each service reads. - Telemetry is opt-in and vendor-neutral and is configured the same way in
every environment — by pointing the standard
OTEL_*variables at a collector. The variables themselves are documented under Observability; choosing and wiring a collector for a deployment is covered in Telemetry.
Two operational concerns get their own pages because they apply across every environment: keeping published runs safe (Backups) and seeing what the services are doing (Telemetry).
Where to go next
Section titled “Where to go next”- Azure: staging & prod — the managed-backend + worker-VM build, for both environments.
- Running — the local mirror: the backend, a worker, and the web console together on one machine, for development.
- Backups — what’s actually at risk (just the backend’s database) and how to protect it.
- Telemetry — choosing and wiring a collector for staging and prod.