Skip to main content
Which ports the stack binds, which of them reach your host, and the variable that changes each one. Extracted from docker-compose.yaml, .env.example, model-server/compose.model-server.yml, and model-server/README.md.
Container port means the port a process binds inside its container. Host port means what Compose publishes on your machine. They differ for FerretDB and for the model-server gateway, and getting them the wrong way round is the most common connection failure in this stack.

Main stack

From the ports: blocks in docker-compose.yaml. “Published?” means Compose maps it onto the host.

FerretDB is 27018 on the host, 27017 in the container

The mapping is "${FERRETDB_HOST_PORT:-27018}:27017". Which port you use depends on where you are connecting from:
  • From your host — a mongosh session, a GUI client, or the API run outside Docker — use 27018. That is what .env.example sets MONGODB_PORT to.
  • From inside the stack, use 27017 against the hostname mongodb. docker-compose.yaml overrides MONGODB_HOST to mongodb and MONGODB_PORT to 27017 on api, arq-worker, and campaign-orchestrator for exactly this reason.
mongodb is a network alias on the ferretdb service, not a separate container. The container itself is voicera_oss_ferretdb.

Postgres and Redis publish nothing

Neither service has a ports: block. They are reachable only from app-network, by service name — postgres:5432 and redis:6379. redis://localhost:6379 in .env.example is for running the API on your host against a Redis you started yourself; it does not describe the Docker stack, where Compose rewrites REDIS_URL to redis://:${REDIS_PASSWORD}@redis:6379. To reach either from your host for debugging, use docker compose exec rather than adding a ports: block:

Model-server stack

A separate compose project (model-server/compose.model-server.yml, project name voicera-model-server) with its own network, model_net. ports: appears exactly once in the whole file. The gateway is the only published port. Model containers bind nothing on the host, which is why this stack can sit beside other stacks without competing for ports. The slot ports never change when you swap a model — a slot is one service on a fixed port, and which model fills it is a folder name in model-server/.env. To reach a model container directly for debugging, model-server/README.md recommends docker compose exec or a temporary ports: mapping rather than a permanent one. Override the upstream addresses only to point a slot at a different host entirely, with STT_UPSTREAM, TTS_UPSTREAM, or LLM_UPSTREAM. They are commented out in model-server/.env.example; unset means “use the Compose service name”.

Default credentials

These ship in .env.example and are what the stack comes up with if you change nothing.
Change all three before any deployment reachable from outside your machine. MinIO and FerretDB are both published on the host by default, so these credentials are live on localhost:9000 and localhost:27018 the moment the stack starts. Redis is not published, but its password is interpolated into REDIS_URL — changing REDIS_PASSWORD alone is enough, because Compose rebuilds the URL from it. See Security hardening.
There is no default VoicEra login. The first super_admin is created by POST /api/v1/users/signup — see Create the first user.

Changing a host port

Set the variable in the root .env and recreate the stack. The container port is unaffected, so nothing inside the stack needs to change:
If you move FERRETDB_HOST_PORT, also update MONGODB_PORT in .env — that value is what a host-side API process connects with. Inside the stack it stays 27017 regardless, because docker-compose.yaml overrides it.