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: