Skip to main content
VoiceGateway merges configuration from three sources with a clear priority order. You can pin critical settings in YAML, manage everything else through the dashboard or MCP server, and override individual values at runtime with environment variables.

Priority order

Environment variables act by substituting into ${VAR} placeholders inside voicegw.yaml before it is parsed (below), plus a handful of settings read from a specific env var directly and bypassing the file entirely (VOICEGW_CONFIG picks which YAML file to load; VOICEGW_DB_PATH/VOICEGW_DB_URL can override cost_tracking.db_path). The DB-vs-YAML merge itself, done by ConfigManager, never looks at environment variables again.

ConfigManager

File: src/voicegateway/core/config_manager.py ConfigManager merges the YAML config and SQLite managed rows into a single GatewayConfig.

Merge rules

YAML always takes precedence. If a provider, model, or project exists in both YAML and SQLite (matched by id), the YAML version wins:
This lets you pin critical configuration in voicegw.yaml and use the dashboard or MCP for everything else, without risk of managed resources overwriting file-based config. The real merge order is projects, then providers, then models: providers merge after projects because a per-project provider row needs somewhere to land, and a managed project row arriving after a managed provider row that targets it would otherwise have nothing to nest into.

The source field

Each ProjectConfig carries a source field indicating origin: For providers and models, the _source key is injected into the config dict (provider_cfg above): it starts from the row’s extra_config JSON, then api_key, base_url, and _source: "db" are set on top of it, so nothing in extra_config can shadow the decrypted key.

YAML configuration

File: src/voicegateway/core/config.py

Environment variable substitution

YAML values containing ${ENV_VAR} are replaced with the corresponding environment variable at load time:
Substitution is recursive: it works inside strings, dicts, and lists. Missing env vars resolve to empty strings. When no explicit path is provided, VoiceGateway searches in this order:
  1. VOICEGW_CONFIG environment variable.
  2. ./voicegw.yaml in the current directory.
  3. ~/.config/voicegateway/voicegw.yaml.
  4. /etc/voicegateway/voicegw.yaml.

Pydantic validation

File: src/voicegateway/schemas/config_schema.py The raw YAML dict is validated against VoiceGatewayConfig before use. Validation errors include field paths and messages:

GatewayConfig dataclass

The parsed config is stored as a GatewayConfig dataclass (src/voicegateway/core/config.py):

Refresh cycle

When the dashboard or MCP server creates, updates, or deletes a managed resource, the config is refreshed without a server restart:
Newly added providers and models are immediately available after the refresh. No restart needed. There is no separate router object to rebuild; the core/router.py/core/model_id.py this page used to describe are gone, and the closest live code, core/model_resolution.py, only parses "provider/model" strings against the provider registry.

Example configuration

voicegw.yaml reference

Full YAML schema with all fields and defaults.

Environment variables

All supported environment variables and their defaults.

Storage

The SQLite tables that back managed configuration.

Security

How managed provider API keys are encrypted at rest.