docker-compose.yml is a local stack: Postgres, the backend, the voice worker
and the console, on one network, with both published ports bound to
127.0.0.1.
Dockerfile and deploys on its own. LiveKit is
external in every case: the worker connects out to LIVEKIT_URL.
Read this before it faces a network
Three facts follow from that:CONSOLE_WRITES_ENABLEDstays off. It defaults tofalsein code. Compose turns it on because compose is local. Anywhere else, leave it off and the write routes refuse.- Your LiveKit signing secret is stored unencrypted. On first boot the
backend copies the LiveKit project from the environment into the
livekit_settingstable, and the Deployment page edits it there. So the secret is in Postgres, in thepgdatavolume, and in any dump you take. Rotating the key in your LiveKit project is the revocation path. CORS_ORIGINS_STRunset means every origin is allowed. Compose sets it tohttp://localhost:5173, but the code default is empty andBACKEND_CORS_ORIGINSreturns["*"]for empty, so a backend started without it accepts browser calls from anywhere. Set it explicitly on any host that is not your own machine.
The three services
agent/fly.toml is set up for Fly: performance-1x, blue/green, and a
kill_timeout of five minutes matching the drain_timeout=300 on
AgentServer. That pairing is what makes a deploy mid-call finish the call
instead of dropping it. The worker also restarts itself when the stored LiveKit
project changes, which is why the compose service carries
restart: unless-stopped. Whatever you deploy onto needs the same: a process
supervisor that brings it back.
The backend runs alembic upgrade head on startup under a Postgres advisory
lock, so concurrent boots serialise. There is no separate migration step, and a
slow database does not stop it booting: it serves tokens from the environment
until Postgres catches up.
The console is static files. The three VITE_ values are compiled in at build
time, so the image is environment-specific: build one per environment, and
never reuse a local image against a remote backend.
Where secrets live
In.env, which is gitignored, except for the LiveKit project once the backend
has copied it into Postgres. Nothing else in this repo persists a credential.