Skip to main content
VoicEra ships as ten containers defined in docker-compose.yaml. This page maps each container to the code it runs, the port it listens on, and the order it starts in. Read it before you scale, restart, or debug a single piece of the stack.
For the layered picture — system context, containers, and the internals of the API and the runtime — see Architecture. This page is the operator’s index of the same set.

Containers versus packages

Containers and Python packages are not one to one. apps/api is a single package that runs as three containers. All three build from apps/api/Dockerfile, share the same image, mount the same code, and differ only in their command. Two more packages are libraries, not containers. apps/providers and apps/telephony are copied into both images and imported: Container names carry the voicera_oss_ prefix (voicera_oss_api, voicera_oss_arq_worker, and so on), as do the four volumes.

Ports

Only five containers publish a host port. Everything else is reachable on the app-network bridge by service name. The full table, including model-server ports, is in Ports and defaults.

Start-up order

depends_on in docker-compose.yaml defines this graph. postgres, redis, and minio gate on a healthcheck (condition: service_healthy); every other edge gates on condition: service_started, which only waits for the process to launch, not to be ready.
api waits for ferretdb to start, not to accept connections, and ferretdb itself only waits for postgres to pass pg_isready. On a cold first boot the API can come up before FerretDB serves queries and log a connection failure. restart: unless-stopped recovers it. If a container is stuck restarting, see Deployment troubleshooting.

Who talks to whom

The runtime never touches FerretDB directly. It reaches every document through the API using a bot JWT minted with INTERNAL_API_KEY — see How it authenticates to the API.