Skip to main content
Deploy VoiceGateway in production with Docker Compose. The daemon serves the HTTP API and the web dashboard on the same port, so one service is enough. Includes persistent storage, health checks, and an optional Ollama sidecar for local LLM inference. For hosting the collector on a VPS, Railway, or Fly.io, see Deployment.

Project structure

Environment variables

Create a .env file with your provider API keys:
Never commit .env files to version control. Add .env to your .gitignore.

Generating a Fernet key

If you do not set VOICEGW_SECRET, VoiceGateway auto-generates one on first run and stores it in the container. Since containers are ephemeral, set this explicitly for production:

Configuration

Create voicegw.yaml:
budget_action (here throttle) does not change gateway behavior by itself: it’s a status label, not an enforcement switch. attach() only records spend against daily_budget. See Projects for what warn/throttle/block actually do, and use guard() if you need a call actually stopped or rerouted once a cap is hit.

Docker Compose

This uses the published image, so the project directory above (just docker-compose.yml, voicegw.yaml, .env) is all you need. Mount voicegw.yaml and point VOICEGW_CONFIG at it. Use 0.24.0 or newer: images at or below 0.22.3 exit at boot without a config file. From 0.24.0 a missing file is only a warning, but a default-config daemon declares no providers, models, or projects, so mount it either way.

Building from source instead

The image above is the right choice for the 3-file project directory above. To build from source instead, you need the full repo, not just those 3 files: the Dockerfile’s build context COPYs pyproject.toml, src/, alembic.ini, and alembic/, none of which exist in a standalone project directory.
Then put your voicegw.yaml and .env at the repo root, and replace the image: line in the Compose file above with:

Starting the services

Cloud-only (API + Dashboard)

This starts the voicegateway service on port 8080. The daemon serves both the HTTP API and the React dashboard SPA on the same port.

With local Ollama

Update voicegw.yaml to use the container hostname:

Fleet collector (Postgres)

To run the self-hosted collector that many LiveKit agents push telemetry to, use the Postgres-backed stack. That compose file ships in the repository and is not one of the three files above, so fetch it first:
This starts a postgres service and a collector service. The collector reads its database from VOICEGW_DB_URL and builds its schema on first start. Set VOICEGW_PG_PASSWORD for anything beyond a local trial. Point the official image at any Postgres directly without compose:

Verifying the deployment

Production considerations

Persistent storage

The voicegw-data volume stores the SQLite database. To back up:

Encryption key persistence

Always set VOICEGW_SECRET in production. If you do not set it, a new Fernet key is generated on first run and stored in the container filesystem. Rebuilding the container loses the key, making encrypted API keys in the database unreadable.

Reverse proxy

For TLS termination, put Nginx or Caddy in front:

Resource limits

Logging

Updating

Bump the image tag in docker-compose.yml (see Auto-update the daemon to automate this on a self-hosted box), then:
If you’re building from source instead, pull the repo and rebuild: