Once the repos are cloned and dependencies are installed, you bring the platform up one service at a time, in order. Each service depends on the one before it: the Backend needs the data layer, the Console needs the Backend, and a fleet worker needs the Backend’s config URL. Follow the four steps below and you’ll have a full local loop that can run an actual call.
This page assumes you’ve already done local setup — cloned the four repos, installed uv and Node, and run uv sync / npm install. If you haven’t, start there.

The local loop at a glance

The arrow that matters most is Fleet → Backend: a worker holds no durable state and fetches everything it needs for a call from the Backend’s config URL at the moment the call starts. Get that one wire right and a call will run.
1

Start MongoDB and Redis

The Backend needs both before it will boot. The quickest way on a laptop is two Docker containers — MongoDB on its default 27017 and Redis on 6379, exactly where the Backend’s .env expects them.
docker run -d --name ori-mongo -p 27017:27017 mongo:7
docker run -d --name ori-redis -p 6379:6379 redis:7
These map straight onto the Backend defaults: MONGODB_URI=mongodb://localhost:27017 and REDIS_URL=redis://localhost:6379. Leave them running in the background.
Already have MongoDB or Redis installed natively? Skip the containers — anything listening on those two ports works. The Backend only cares about the URIs in its .env.
2

Run the Backend

From the vox-backend repo, copy .env.example to .env and set at least a JWT_SECRET. The defaults already point at your local MongoDB and Redis.
MONGODB_URI=mongodb://localhost:27017
MONGODB_DB=voxbridge
REDIS_URL=redis://localhost:6379
JWT_SECRET=dev-secret-change-me
JWT_ALGORITHM=HS256
JWT_EXPIRE_MINUTES=1440
LOG_LEVEL=INFO
Start the API on port 8080 via the run script (which wraps uvicorn):
./scripts/run.sh
The app only boots when its required settings are present, so a clean start confirms MongoDB and Redis are reachable. With a fresh database, seed the first admin user so you can log into the Console:
uv run python scripts/seed_admin.py
MONGODB_DB is voxbridge and the Dialler shares the exact same database. On a laptop you usually don’t run the Dialler at all (see Advanced) — but if you do, point it at this same DB.
3

Run the Console

From the vox-frontend repo, make sure VITE_API_URL points at your local Backend. For Ori the brand .env is committed in the repo; for local work the only value you need to override is the API URL:
VITE_API_URL=http://localhost:8080
Start the Vite dev server:
npm run dev
Vite serves the SPA (default http://localhost:5173) and every admin call it makes goes to VITE_API_URL. If logins fail with network errors, this is almost always the value to check.
4

Run one voice-fleet worker

From the vox-agents repo, copy .env.example to .env. The single most important line is the config URL pointing back at your Backend, plus the provider keys for the speech stack and somewhere to put recordings:
VOXBRIDGE_CONFIG_URL=http://localhost:8080/api/v1/config

# Speech providers — fill in the ones your test bot uses
DEEPGRAM_API_KEY=...
SONIOX_API_KEY=...
OPENAI_API_KEY=...
GOOGLE_API_KEY=...
ELEVENLABS_API_KEY=...
SARVAM_API_KEY=...

# Recordings — local MinIO (or any S3-compatible storage)
MINIO_ENDPOINT=localhost:9000
MINIO_ACCESS_KEY=...
MINIO_SECRET_KEY=...
MINIO_BUCKET=recordings
MINIO_SECURE=false

AUDIO_SAMPLE_RATE=16000
MAX_CONCURRENT_CALLS=30
LOG_LEVEL=INFO
For local development you want one worker on a TCP port (not the production Unix-socket model). The dev run script does multiple workers by default; for a single, easy-to-watch worker, run uvicorn directly:
uv run uvicorn voxcore.app:app --host 0.0.0.0 --port 8000 --workers 1 --app-dir src
Recordings need real object storage. If you don’t run a local MinIO, the call itself will still work but the recording upload step will fail. Either start a MinIO container exposing localhost:9000 with a recordings bucket, or point the MINIO_* vars at any S3-compatible endpoint you can reach.

How config flows to the worker

A fleet worker is stateless by design. When a call begins, the worker calls the Backend at VOXBRIDGE_CONFIG_URL to fetch the runtime config for that bot — prompts, voice settings, the tool set, and any CRM data for the contact — and runs the conversation entirely from what comes back. Nothing about the bot lives on the worker between calls. This is why the order of the four steps matters: the Backend must be up and reachable at the config URL before a worker can do anything useful. If a local call errors immediately, check that VOXBRIDGE_CONFIG_URL resolves and that the bot you’re testing exists in the Backend’s database.

Verify it works

A quick three-point check confirms the whole loop is wired up:
1

Console loads and logs in

Open the Vite URL (default http://localhost:5173), and log in with the admin user you seeded in step 2. If the dashboard loads, the Console → Backend wire (VITE_API_URL) is good.
2

The Backend is healthy

curl http://localhost:8080/health
A healthy response means MongoDB and Redis are reachable and the API booted with its required settings.
3

The worker is healthy

curl http://localhost:8000/health
/health reports a single worker’s status. (In production you’d also hit /health/fleet to aggregate every worker over its socket — but with one local worker on a TCP port, /health is all you need.)
With all three green, the easiest way to actually exercise a call locally is the in-browser widget (POST /livekit/widget) or a direct WebSocket test against /ws/{bot_id} — no carrier, no phone number, no SIP. Create a bot in the Console first so the worker has a config to fetch.

Advanced: LiveKit and the Dialler

The two services below complete the platform but need real telephony to be useful, so they’re rarely run on a laptop. Reach for them only when you’re testing SIP or campaign behaviour specifically.
LiveKit handles SIP trunks and the media plane for inbound DIDs, outbound dial, and the browser widget. It runs as a Docker Compose stack (livekit + livekit-sip), brought up by the helper in the vox-agents repo:
./infra/livekit/setup.sh
For real phone calls you also need carrier SIP trunks configured and, on a cloud host, use_external_ip plus a webhook for inbound dispatch. None of that is needed for the local widget/WebSocket path above — which is exactly why that path is the easy way to test a call on your machine.

Where to go next

Local setup

Prerequisites, cloning the four repos, and installing dependencies — the step before this one.

Repository map

The four repositories, their boundaries, and the contracts between them.

Deploy it

The Bitbucket → Jenkins pipeline, host deployment, and the production run model.

System architecture

How the four services, the data layer, and the telephony plane fit together.