Skip to content

Docker quickstart

The Docker quickstart is the fastest way to get the control plane running on a single host. Everything runs in Docker Compose (Postgres, the control plane, the BFF, the embedded admin guide, and the dashboard) so the only thing you need on the host is Docker itself. About ten minutes from "download the bundle" to "logged into the dashboard."

If you're evaluating the control plane for the first time, this is the right path. It's also a perfectly reasonable production deployment for small-to-medium teams; a lot of our customers run it as-is.

📌 Worth knowing. 0.8.0-beta.4 is a pre-release. Recommended for evaluation, staging, and early-adopter integration testing; for production traffic, pin a stable tag once one is published.


Before you start

You'll need:

  1. A host machine running Linux or macOS, with Docker 24+ and Docker Compose v2 installed. 4 GB of free RAM is the floor; 8 GB is comfortable.
  2. The release tarball: vidai-quickstart-<version>.tar.gz. Available from github.com/vidaiUK/vidai-quickstart/releases/latest.
  3. A vidai.license file. Community admins generate one self-serve at portal.vidai.uk; Scale and Enterprise admins receive theirs by email after the licensing conversation. The control plane does not start without a vidai.license file in place.

Step 1: Download the release

VERSION=0.8.0-beta.4
curl -LO https://github.com/vidaiUK/vidai-quickstart/releases/download/v${VERSION}/vidai-quickstart-${VERSION}.tar.gz
# Optional: also fetch the cosign signature bundle for verification (see Step 2).
curl -LO https://github.com/vidaiUK/vidai-quickstart/releases/download/v${VERSION}/vidai-quickstart-${VERSION}.tar.gz.bundle

Extract the tarball and move into it:

tar xzf vidai-quickstart-${VERSION}.tar.gz
cd vidai-quickstart-${VERSION}

Inside, you'll find:

README.md            this bundle's authoritative install instructions
VERIFY.md            optional cosign verification walkthrough
LICENSE              the commercial licence agreement (Scale / Enterprise)
LICENSE-COMMUNITY    the Community Edition licence agreement
LICENSES/            third-party licence texts (Apache, MIT, BSD)
docker-compose.yml   five services: postgres, vidai-server, bff, docs, frontend
.env.example         configuration template
setup.sh             interactive setup — generates .env (run this first)
vidai                operator CLI — daily ops, upgrades, backup/restore
config/              vidai-server.yaml + supporting files

The bundle's own README.md is the authoritative command-line reference. This page tells the same story in walkthrough form; if you ever see a discrepancy, the bundle README wins (it ships with the bundle, so it's matched to the exact version you downloaded).


Step 2: Verify the bundle (optional)

Every release artefact is signed by Vidai UK Limited with cosign, and the signatures are recorded on the public Sigstore transparency log (Rekor). Verification is optional — the product runs identically whether or not you verify — but if your environment requires supply-chain attestation, this is how:

curl -o cosign.pub https://vidai.uk/.well-known/cosign.pub
cosign verify-blob --key cosign.pub \
    --bundle vidai-quickstart-${VERSION}.tar.gz.bundle \
    vidai-quickstart-${VERSION}.tar.gz
# → "Verified OK"

The bundled VERIFY.md walks through the full three-layer verification: tarball, runtime images, and the Server binary inside the image.


Step 3: Drop the licence file in place

Copy your vidai.license next to docker-compose.yml:

cp /path/to/your/vidai.license ./

setup.sh looks here (and in ./vidai_license/) for the file. Until it's in place, the control plane will not start.


Step 4: Run setup.sh

./setup.sh

The script confirms the licence file, asks a few short questions, then writes a working .env file with the answers + auto-generated secrets:

  • Licence: setup auto-detects the vidai.license file you dropped in Step 3. If you'd rather paste the licence value at the prompt instead, that path still works.
  • Postgres mode: [B]undled runs Postgres in Compose alongside the control plane; [E]xternal lets you bring your own (RDS, your corporate DB cluster, etc.). Bundled is the default and the easier path; pick External when your ops policy requires it.
  • Service ports: defaults are 80 (dashboard), 8000 (BFF), 3000 (control plane), 9091 (Prometheus metrics — Enterprise only), 5432 (Postgres). Override only if your host already uses one of those.
  • Confirmation: setup writes .env with the values it collected and shows you a summary.

Strong random secrets (the database password, admin keys, JWT signing key) are generated by setup.sh and written into .env (with chmod 600). You don't need to touch those by hand; the bundle's other moving parts read them out of .env automatically.

💡 Pro tip. The bundle ships with a sensible default Compose project name. If you're running multiple VIDAI deployments on the same host (rare, usually for testing), set COMPOSE_PROJECT_NAME in .env before starting so their networks and volumes don't collide.

Bringing your own Postgres

If you chose [E]xternal, setup.sh asks for the host, port, database, username, and password, and verifies connectivity before writing .env. Before you run setup in External mode, prepare the database side:

CREATE USER vidai_user WITH PASSWORD '<strong-password>';
CREATE DATABASE vidai OWNER vidai_user;
GRANT CONNECT ON DATABASE vidai TO vidai_user;
\c vidai
GRANT USAGE, CREATE ON SCHEMA public TO vidai_user;
GRANT INSERT, UPDATE, SELECT, DELETE ON ALL TABLES IN SCHEMA public TO vidai_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT INSERT, UPDATE, SELECT, DELETE ON TABLES TO vidai_user;

vidai_user doesn't need superuser. No Postgres extensions are required: neither pgcrypto nor uuid-ossp, which is good news for managed-Postgres services that don't permit extension installation.

For small managed-RDS instances (e.g. db.t4g.micro), set lower connection-pool sizes in your .env after setup.sh finishes:

BFF_DB_POOL_SIZE=5
BFF_DB_MAX_OVERFLOW=10

This keeps the control plane from saturating the database's connection limit during traffic spikes.


Step 5: Start the stack

./vidai start

This boots all five services and captures per-service startup logs to ./setup-logs/<timestamp>/ while the stack comes up. Capture stops automatically once everything reports healthy. The first run pulls the container images, which takes a minute or two depending on your network.

The services come up in order: Postgres first (the others wait for its healthcheck to pass), then the control plane and BFF, then the docs container and dashboard.

./vidai status

shows the live state — a colour-coded per-service health table, the resolved licence edition, and the running version. All five services should reach healthy within a couple of minutes. If one stays in starting or flips to unhealthy, jump to Troubleshooting below — the path to the relevant setup-logs/ file is your first stop.

💡 Pro tip. ./vidai and raw docker compose are equivalent — every ./vidai command wraps a Compose invocation. Use ./vidai for the friendly affordances (setup-log capture, healthy-wait, edition display, upgrade orchestration); reach for native Compose when you need something niche.


Step 6: First sign-in

Open http://localhost (or your chosen frontend port) in a browser. The first-boot credentials are:

The console will force you to set a real password before it shows you anything else. Pick a strong one and store it in your team's secret manager; there's no recovery flow for the very first admin password.

⚠️ Watch out. [email protected] / changeme123 is the seed admin and it carries a must change password flag. The console blocks normal use until you rotate it. Don't try to disable that flag in the database; it's the safety mechanism that keeps a leaked default password from turning into a production breach.

Before you start sending real traffic through, there's one more thing to configure.

Configure the rate-card server

The control plane prices every request as it returns, but only if it knows what each model costs. The rate-card server is the data source. Until it's configured, request logs still flow, but the cost-attribution columns stay empty.

  • Community — your rate-card server URL and API credentials are shown alongside the licence download at portal.vidai.uk. Free public server, best-effort SLA, refreshed on a standard cadence — enough for cost visibility, budgets, per-team attribution and spend-circuit alerts.
  • Scale / Enterprise — your rate-card server URL and credentials arrived in the same email as the licence. Enterprise customers can swap in their own self-hosted rate-card server later if they prefer.

Paste the URL and credentials into the dashboard at Settings → Rate cards → Configure. Once configured, the full cost engine activates: per-request pricing, team / model / project attribution, budgets, and spend-circuit alerts.

After that, follow the Getting started walk-through to mint your first API key and make your first proxied request.


Calling the control plane from your application

Once you've signed in and minted an API key, your applications point at the proxy on its dedicated port:

curl http://localhost:3000/v1/chat/completions \
  -H "Authorization: Bearer YOUR-API-KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "your-configured-model",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

API keys live on the API Keys page in the console; provider credentials (the OpenAI / Anthropic / Google keys the control plane uses upstream) live on Providers. Both are managed centrally inside the console after install; there's nothing more to do at the host layer.


The five services

Service Port Purpose
frontend 80 Admin dashboard, plus the embedded admin guide at /guide/.
bff 8000 Backend API the dashboard talks to.
vidai-server 3000 The control plane itself; your apps call this.
vidai-server 9091 Prometheus metrics endpoint (Enterprise only); see Observability.
docs — Embedded admin guide; served via the frontend, no port of its own.
postgres 5432 Database (bundled mode only).

You only ever expose port 80 to your application traffic in the simplest setup; the rest are inter-service. In production you'd typically front everything with a reverse proxy (nginx, Caddy, your cloud load balancer) that terminates HTTPS and routes the dashboard and the control plane to their respective service ports.


Common operations

The ./vidai CLI wraps the operations you'll reach for day to day. Native docker compose always works too.

Check health

./vidai status                             # colour-coded per-service health + licence + version

View logs

./vidai logs                               # tail every service
./vidai logs vidai-server                  # tail one service
docker compose logs --tail 200 bff         # one-shot, last N lines

./vidai start writes per-service boot logs to ./setup-logs/<timestamp>/, frozen at boot time. For live logs after that, use the commands above.

Start, stop, restart

./vidai start                              # boot with setup-log capture + healthy-wait
./vidai stop                               # stop services, keep data
./vidai restart                            # stop + start

Restart one service

docker compose restart bff

The other services keep running. Useful when the BFF gets into a bad state and you don't want to bounce the whole stack.

Tear down

./vidai down                               # remove containers, keep data
./vidai down -v                            # remove containers AND data (DESTRUCTIVE)

⚠️ Watch out. down -v is destructive. It removes the bundled Postgres volume; audit logs, telemetry, all user accounts go with it. The CLI triple-confirms before running it (three sequential prompts, each requiring a different exact string). For automation, set VIDAI_CONFIRM_DESTROY=YES_I_REALLY_MEAN_IT to bypass. Use ./vidai down (no -v) for normal stop-and-restart cycles.


Backups and restore

./vidai backup takes a snapshot anytime, in case you want a known-good restore point:

./vidai backup
# → ./backups/manual-<timestamp>/

Each backup contains:

  • postgres.dump — pg_dump of the database (bundled-Postgres mode only).
  • vidai_data.tar.gz — the control plane's /data volume.
  • env.snapshot — verbatim copy of your .env (licence, secrets, version pins at backup time).
  • manifest.json — what's inside, versions running.

Manual backups are never auto-deleted. The implicit pre-upgrade backups taken by ./vidai upgrade are rotated by VIDAI_BACKUP_RETENTION (default 5 most recent), with one exception: backups that have been used for an actual restore are kept as forensic evidence.

To store backups outside the install directory (separate disk, NAS mount), set VIDAI_BACKUP_DIR in .env to an absolute path.

📌 Worth knowing. Your data volumes survive image swaps. Pulling new images doesn't touch the bundled Postgres volume; for BYO Postgres, your data is yours to manage as you always have. Only ./vidai down -v deletes data.

For BYO-Postgres mode, your cloud provider handles database backup — ./vidai backup skips Postgres and records this in the manifest. To take an ad-hoc Postgres dump in BYO mode:

docker run --rm \
    -e PGHOST=... -e PGUSER=... -e PGPASSWORD=... \
    postgres:16-alpine pg_dump -Fc vidai > my-backup.dump

Restoring from a backup

./vidai stop
./vidai restore ./backups/<backup-dir>/

./vidai restore validates the backup, captures the current (broken) state to <backup>/restored-on-<now>/failure-snapshot/ as forensic evidence, stops the stack, restores Postgres and the /data volume, restores .env (which brings back the old version pins automatically), pulls the corresponding images, and waits for healthy convergence.

Cross-major restore is refused by default — schema migrations don't roll backwards across majors. Pass --force if you understand the risk:

./vidai restore ./backups/<path> --force

Upgrades

You'll hear about upgrades by email from [email protected]. The email tells you which type of upgrade it is and the exact commands to run. Two types, described below.

In-place patch upgrade

Same component versions; new content layered into the images (security patches, base-OS updates). Nothing in your .env changes:

docker compose pull        # fetch the new image content for the current tags
./vidai restart            # apply

Bundled upgrade

Component versions change. Most common case. You'll receive a new bundle archive — and the whole upgrade is one command: ./vidai upgrade <from-version> <to-version>.

# 1. Extract the new bundle next to your existing install.
tar xzf vidai-quickstart-<new-version>.tar.gz
cp /path/to/old-install/.env vidai-quickstart-<new-version>/

# 2. Move into the new bundle:
cd vidai-quickstart-<new-version>

# 3. Run the upgrade:
./vidai upgrade <old-version> <new-version>

What ./vidai upgrade does for you:

  • Offers a pre-upgrade backup (default Y, recommended).
  • Validates that <old-version> matches your current .env and <new-version> matches this bundle's .env.example. Refuses if you extracted the wrong bundle.
  • Applies the new bundle's SERVER_VERSION, BFF_VERSION, and FRONTEND_VERSION to your .env. Licence, secrets, port choices, and Postgres mode untouched.
  • Pulls the new images and brings the stack up.
  • Waits up to 3 minutes for every service to become healthy.
  • Logs everything to ./upgrade-logs/<timestamp>-<from>-to-<to>/ so you can send the folder to support if something goes wrong.

Volume names are stable across upgrades: Docker Compose's project name is pinned to vidai, so the Postgres and control plane data volumes carry the same names regardless of which directory you extracted the bundle into. Old install directories can be deleted after the upgrade completes.

Database schema migrations run automatically on startup; there's no manual migration step. If a release ever requires manual data migration, the upgrade email will say so explicitly and walk you through it.

Rolling back

If an upgrade misbehaves and you want to back out, restore the pre-upgrade backup that ./vidai upgrade took for you:

./vidai stop
./vidai restore ./backups/auto-pre-upgrade-<from>-to-<to>-<timestamp>/

This brings back the previous version pins, secrets, and data state in one step. Cross-major restores are refused without --force (see Restoring from a backup).


Troubleshooting

Services won't become healthy

./vidai status                             # see which services are unhealthy
./vidai logs <service>                     # check that service's logs
ls ./setup-logs                            # past boot attempts

If a service fails to become healthy within 3 minutes, ./vidai start prints the tail of that service's log and the path to the full file in ./setup-logs/<timestamp>/. That folder is the first thing to attach if you email support.

Common causes:

  • vidai-server unhealthy: the licence is missing, invalid, or expired. Confirm vidai.license is next to docker-compose.yml (or in ./vidai_license/) and matches what setup.sh loaded. ./vidai logs vidai-server shows the licence-resolution lines explicitly.
  • bff unhealthy: the database isn't reachable. In bundled mode, ./vidai status should show Postgres healthy; if not, check its logs. In External mode, re-verify connectivity from the BFF container with docker compose exec bff sh and a psql test.
  • frontend unhealthy: the BFF or docs container didn't come up. Fix the upstream service first; ./vidai restart brings them all back in order.

Port conflicts

If ./vidai start reports a port already in use, edit the relevant *_PORT variable in .env and re-run ./vidai start. Or re-run ./setup.sh, which prompts for free ports interactively.

Authentication errors between services after manual edits

Most often VIDAI_ADMIN_SECRET got out of sync between services. The value is generated once by setup.sh and must match across every service that references it. If you've edited .env by hand, ensure both the control plane and BFF reference the same value, then ./vidai restart.


Where to go next

  • Getting started: what to do inside the console once you're signed in: minting an API key, configuring your first provider, making your first proxied request.
  • Providers: add OpenAI / Anthropic / Google / etc. credentials so the control plane can route upstream.
  • Settings: ongoing operational knobs, including worker flags and the rate-card sync.
  • For other deployment models (binary mode, airgap, Kubernetes), start at the Install index.